feat(dedicated): bandwidth + private networking restructure

Bandwidth: dropped the "10 Gbps unmetered (fair-use)" tier — at our
\$295/mo it was a margin trap (Evocative bills 95th percentile at
\$0.48/Mbps; saturated 10G = \~\$4,560/mo cost). Replaced with a
"10 Gbps + 500 TB" tier at \$345/mo. 500 TB covers 99% of legitimate
heavy use; abuse customers self-select into a metered package or
get billed for overage.

New bandwidth ladder:
  1 Gbps unmetered (baseline)            \$0
  10 Gbps + 10 TB                        \$45
  10 Gbps + 50 TB                        \$95
  10 Gbps + 100 TB                       \$175
  10 Gbps + 500 TB  (NEW, replaces ∞)    \$345

Private Networking: new group, separate from public Bandwidth.
Customer can pick a private intra-rack link speed independent of
their public uplink. Traffic stays on our internal switch fabric
and isn't metered. Flat monthly per the brainstorm decision (no
setup fees).

  1 Gbit private (included, default)     \$0
  10 Gbit private                        \$25
  40 Gbit private                        \$75

Attached to all 8 14th-gen plans.

Updated test count: now 10 Dedicated 14th Gen config groups (was 9).
14/14 tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-26 19:00:50 -04:00
parent d09224c35c
commit f464e1ad48
2 changed files with 71 additions and 13 deletions

View File

@@ -476,7 +476,7 @@ class ConfigOptionSeeder extends Seeder
['label' => '10 Gbps + 10 TB included', 'value' => '10g-10tb', 'monthly' => 45.00], ['label' => '10 Gbps + 10 TB included', 'value' => '10g-10tb', 'monthly' => 45.00],
['label' => '10 Gbps + 50 TB included', 'value' => '10g-50tb', 'monthly' => 95.00], ['label' => '10 Gbps + 50 TB included', 'value' => '10g-50tb', 'monthly' => 95.00],
['label' => '10 Gbps + 100 TB included', 'value' => '10g-100tb', 'monthly' => 175.00], ['label' => '10 Gbps + 100 TB included', 'value' => '10g-100tb', 'monthly' => 175.00],
['label' => '10 Gbps unmetered (fair-use AUP)', 'value' => '10g-unmetered-fair-use', 'monthly' => 295.00], ['label' => '10 Gbps + 500 TB included', 'value' => '10g-500tb', 'monthly' => 345.00],
]); ]);
$gen14Bandwidth->plans()->syncWithoutDetaching($gen14AllPlans); $gen14Bandwidth->plans()->syncWithoutDetaching($gen14AllPlans);
@@ -509,6 +509,30 @@ class ConfigOptionSeeder extends Seeder
$gen14Ipv4->plans()->syncWithoutDetaching($gen14AllPlans); $gen14Ipv4->plans()->syncWithoutDetaching($gen14AllPlans);
// ─── Dedicated 14th Gen — Private Networking ────────────────────
// Separate from public bandwidth. Intra-rack private link with no
// bandwidth metering — traffic never traverses the public internet,
// so only the link speed itself is billable. Flat monthly, no setup.
$gen14PrivateNet = PlanConfigGroup::updateOrCreate(
['name' => 'Dedicated 14th Gen — Private Networking'],
[
'description' => 'Optional dedicated private network link for server-to-server traffic. Unmetered (traffic stays on our internal switch fabric).',
'mode' => 'preset',
'service_type' => 'dedicated',
'is_active' => true,
'sort_order' => 46,
],
);
$gen14PrivateNetOption = $this->seedRadioOption($gen14PrivateNet, 'Private Network', false, 1);
$this->seedValues($gen14PrivateNetOption, [
['label' => '1 Gbit private (included)', 'value' => '1g', 'monthly' => 0, 'is_default' => true],
['label' => '10 Gbit private', 'value' => '10g', 'monthly' => 25.00],
['label' => '40 Gbit private', 'value' => '40g', 'monthly' => 75.00],
]);
$gen14PrivateNet->plans()->syncWithoutDetaching($gen14AllPlans);
// ─── Dedicated 14th Gen — LFF Drive Bays ──────────────────────── // ─── Dedicated 14th Gen — LFF Drive Bays ────────────────────────
$gen14Lff = PlanConfigGroup::updateOrCreate( $gen14Lff = PlanConfigGroup::updateOrCreate(
['name' => 'Dedicated 14th Gen — LFF Drive Bays'], ['name' => 'Dedicated 14th Gen — LFF Drive Bays'],

View File

@@ -94,9 +94,11 @@ test('detail page exposes chassis-specific config groups', function (): void {
$configGroups = collect($response->viewData('page')['props']['configGroups']); $configGroups = collect($response->viewData('page')['props']['configGroups']);
$names = $configGroups->pluck('name'); $names = $configGroups->pluck('name');
// R740xd variants get the R740xd CPU group, not the standard CPU group // All chassis (including R740xd variants) now use the standard CPU
expect($names)->toContain('Dedicated 14th Gen — CPU Upgrade (R740xd)'); // group; the (R740xd) group is detached and the route-level
expect($names)->not->toContain('Dedicated 14th Gen — CPU Upgrade'); // cpu_premium filter handles Platinum visibility.
expect($names)->toContain('Dedicated 14th Gen — CPU Upgrade');
expect($names)->not->toContain('Dedicated 14th Gen — CPU Upgrade (R740xd)');
// Universal groups (RAM, OS, Bandwidth, IPv4) attach to all 8 chassis // Universal groups (RAM, OS, Bandwidth, IPv4) attach to all 8 chassis
expect($names)->toContain('Dedicated 14th Gen — RAM Upgrade'); expect($names)->toContain('Dedicated 14th Gen — RAM Upgrade');
@@ -105,29 +107,61 @@ test('detail page exposes chassis-specific config groups', function (): void {
expect($names)->toContain('Dedicated 14th Gen — IPv4 Block'); expect($names)->toContain('Dedicated 14th Gen — IPv4 Block');
}); });
test('non-R740xd chassis get the standard CPU group', function (): void { test('Platinum CPU is filtered out for chassis that do not support it', function (): void {
$response = $this->get('/dedicated-servers/r440-4lff'); // R440 and R540 should NOT see Platinum 8280.
$response->assertOk(); foreach (['r440-4lff', 'r540-8lff'] as $slug) {
$response = $this->get("/dedicated-servers/{$slug}");
$cpuGroup = collect($response->viewData('page')['props']['configGroups'])
->firstWhere('name', 'Dedicated 14th Gen — CPU Upgrade');
$values = collect($cpuGroup['options'][0]['values'])->pluck('value')->all();
expect($values)->not->toContain('platinum-8280');
}
$names = collect($response->viewData('page')['props']['configGroups'])->pluck('name'); // R640, R740, R740xd variants SHOULD see Platinum.
foreach (['r640-8sff', 'r740-16sff', 'r740xd-24sff', 'r740xd-12lff', 'r640-10nvme', 'r740xd-24nvme'] as $slug) {
expect($names)->toContain('Dedicated 14th Gen — CPU Upgrade'); $response = $this->get("/dedicated-servers/{$slug}");
expect($names)->not->toContain('Dedicated 14th Gen — CPU Upgrade (R740xd)'); $cpuGroup = collect($response->viewData('page')['props']['configGroups'])
->firstWhere('name', 'Dedicated 14th Gen — CPU Upgrade');
$values = collect($cpuGroup['options'][0]['values'])->pluck('value')->all();
expect($values)->toContain('platinum-8280');
}
}); });
test('seeder creates the 9 dedicated 14th-gen config groups', function (): void { test('1.5 TB RAM is filtered out for chassis with 16 DIMM slots', function (): void {
// R440 and R540 cap at 1 TB.
foreach (['r440-4lff', 'r540-8lff'] as $slug) {
$response = $this->get("/dedicated-servers/{$slug}");
$ramGroup = collect($response->viewData('page')['props']['configGroups'])
->firstWhere('name', 'Dedicated 14th Gen — RAM Upgrade');
$values = collect($ramGroup['options'][0]['values'])->pluck('value')->all();
expect($values)->not->toContain('1536');
expect($values)->toContain('1024');
}
// 24-DIMM-slot chassis should see 1.5 TB.
foreach (['r640-8sff', 'r740xd-24nvme'] as $slug) {
$response = $this->get("/dedicated-servers/{$slug}");
$ramGroup = collect($response->viewData('page')['props']['configGroups'])
->firstWhere('name', 'Dedicated 14th Gen — RAM Upgrade');
$values = collect($ramGroup['options'][0]['values'])->pluck('value')->all();
expect($values)->toContain('1536');
}
});
test('seeder creates the 10 dedicated 14th-gen config groups', function (): void {
$names = PlanConfigGroup::query() $names = PlanConfigGroup::query()
->where('name', 'like', 'Dedicated 14th Gen%') ->where('name', 'like', 'Dedicated 14th Gen%')
->pluck('name') ->pluck('name')
->all(); ->all();
expect(count($names))->toBe(9); expect(count($names))->toBe(10);
expect($names)->toContain('Dedicated 14th Gen — CPU Upgrade'); expect($names)->toContain('Dedicated 14th Gen — CPU Upgrade');
expect($names)->toContain('Dedicated 14th Gen — CPU Upgrade (R740xd)'); expect($names)->toContain('Dedicated 14th Gen — CPU Upgrade (R740xd)');
expect($names)->toContain('Dedicated 14th Gen — RAM Upgrade'); expect($names)->toContain('Dedicated 14th Gen — RAM Upgrade');
expect($names)->toContain('Dedicated 14th Gen — Operating System'); expect($names)->toContain('Dedicated 14th Gen — Operating System');
expect($names)->toContain('Dedicated 14th Gen — Bandwidth'); expect($names)->toContain('Dedicated 14th Gen — Bandwidth');
expect($names)->toContain('Dedicated 14th Gen — IPv4 Block'); expect($names)->toContain('Dedicated 14th Gen — IPv4 Block');
expect($names)->toContain('Dedicated 14th Gen — Private Networking');
expect($names)->toContain('Dedicated 14th Gen — LFF Drive Bays'); expect($names)->toContain('Dedicated 14th Gen — LFF Drive Bays');
expect($names)->toContain('Dedicated 14th Gen — SFF Drive Bays'); expect($names)->toContain('Dedicated 14th Gen — SFF Drive Bays');
expect($names)->toContain('Dedicated 14th Gen — NVMe Drive Bays'); expect($names)->toContain('Dedicated 14th Gen — NVMe Drive Bays');