Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions frontend/src/__tests__/settings-service-ids.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ describe('Settings Azure/GCP service ids (issue #613)', () => {

it('every Azure/GCP id the cards save and the override list offers is a backend service slug', async () => {
await loadGlobalSettings();
// Only edited rows are saved (#539): edit each Azure/GCP term.
document.querySelectorAll<HTMLSelectElement>('select[id^="azure-"][id$="-term"], select[id^="gcp-"][id$="-term"]')
.forEach(sel => { sel.value = sel.value === '1' ? '3' : '1'; });
await saveGlobalSettings(new Event('submit'));
const slugs = backendServiceSlugs();
const offered: string[] = [];
Expand Down Expand Up @@ -148,6 +151,27 @@ describe('Settings Azure/GCP service ids (issue #613)', () => {
expect(puts.find(c => c.provider === 'gcp' && c.service === 'cache')!.cfg['term']).toBe(1);
});

it('writes a re-keyed row in full once, then only its changed fields', async () => {
mockConfig([
{ provider: 'azure', service: 'vm', term: 1, payment: 'monthly', enabled: true, coverage: 80, ramp_schedule: 'r1' },
]);
await loadGlobalSettings();
await saveGlobalSettings(new Event('submit'));
const first = servicePuts().filter(c => c.provider === 'azure' && c.service === 'compute');
expect(first).toHaveLength(1);
expect(first[0]!.cfg).toEqual(expect.objectContaining({ ramp_schedule: 'r1', coverage: 80 }));
(api.updateServiceConfig as jest.Mock).mockClear();

await saveGlobalSettings(new Event('submit'));
expect(servicePuts()).toEqual([]);

(document.getElementById('azure-vm-term') as HTMLSelectElement).value = '3';
await saveGlobalSettings(new Event('submit'));
const second = servicePuts();
expect(second).toHaveLength(1);
expect(Object.keys(second[0]!.cfg).sort()).toEqual(['provider', 'service', 'term']);
});

it('prefers a row already stored under the current id over its legacy twin', async () => {
mockConfig([
{ provider: 'azure', service: 'vm', term: 1, payment: 'monthly', enabled: true, coverage: 80 },
Expand Down
36 changes: 34 additions & 2 deletions frontend/src/__tests__/settings-service-payment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ function mountRealIndexHtml(): void {
document.body.innerHTML = new DOMParser().parseFromString(html, 'text/html').body.innerHTML;
}

// Flip every per-service term card so each row counts as edited and is PUT.
function editEveryServiceTerm(): void {
document.querySelectorAll<HTMLSelectElement>('select[id^="aws-"][id$="-term"], select[id^="azure-"][id$="-term"], select[id^="gcp-"][id$="-term"]')
.forEach(sel => { sel.value = sel.value === '1' ? '3' : '1'; });
}

type ServiceCall = { provider: string; service: string; payment: string };

function serviceCalls(): ServiceCall[] {
Expand Down Expand Up @@ -76,6 +82,7 @@ describe('Settings Save payment tokens (issue #545)', () => {

async function save(): Promise<void> {
await loadGlobalSettings();
editEveryServiceTerm();
await saveGlobalSettings(new Event('submit'));
}

Expand All @@ -93,6 +100,7 @@ describe('Settings Save payment tokens (issue #545)', () => {

it('sends the Azure option the user picked, and GCP monthly whatever the global default is', async () => {
await loadGlobalSettings();
editEveryServiceTerm();
(document.getElementById('azure-vm-payment') as HTMLSelectElement).value = 'monthly';
(document.getElementById('setting-default-payment') as HTMLSelectElement).value = 'partial-upfront';
await saveGlobalSettings(new Event('submit'));
Expand Down Expand Up @@ -121,8 +129,15 @@ describe('Settings Save payment tokens (issue #545)', () => {
await expect(loadGlobalSettings()).resolves.not.toThrow();
expect(document.getElementById('gcp-storage-term')).toBeNull();
expect((document.getElementById('gcp-compute-term') as HTMLSelectElement).value).toBe('1');
editEveryServiceTerm();
await saveGlobalSettings(new Event('submit'));
expect(serviceCalls().filter(c => c.provider === 'gcp').every(c => c.payment === 'monthly')).toBe(true);
// gcp/compute has a stored row, so only its term goes out; the new rows carry monthly.
const gcp = serviceCalls().filter(c => c.provider === 'gcp');
expect(gcp.map(c => [c.service, c.payment])).toEqual([
['compute', undefined],
['relational-db', 'monthly'],
['cache', 'monthly'],
]);
expect(serviceCalls().some(c => c.service === 'storage')).toBe(false);
});

Expand Down Expand Up @@ -160,7 +175,8 @@ describe('Settings Save payment tokens (issue #545)', () => {

await saveGlobalSettings(new Event('submit'));

expect(api.updateServiceConfig).toHaveBeenCalledTimes(17);
// Only the two edited rows are PUT (#539).
expect(api.updateServiceConfig).toHaveBeenCalledTimes(2);
expect(mockShowToast).toHaveBeenCalledTimes(1);
expect(mockShowToast).toHaveBeenCalledWith({
message: 'Settings saved, but 1 service failed: azure/compute: invalid payment option',
Expand Down Expand Up @@ -203,6 +219,7 @@ describe('Settings Save payment tokens (issue #545)', () => {
throw provider === 'aws' && service === 'ec2' ? new Error('boom') : denied();
});
await loadGlobalSettings();
editEveryServiceTerm();

await saveGlobalSettings(new Event('submit'));

Expand All @@ -218,6 +235,7 @@ describe('Settings Save payment tokens (issue #545)', () => {
await loadGlobalSettings();
const notify = document.getElementById('setting-notification-days') as HTMLInputElement;
notify.value = '9';
editEveryServiceTerm();

await saveGlobalSettings(new Event('submit'));

Expand All @@ -244,6 +262,20 @@ describe('Settings Save payment tokens (issue #545)', () => {
expect(serviceCalls().find(c => c.provider === 'azure' && c.service === 'compute')!.payment).toBe(expected);
});

it('a stored legacy payment the card normalised on load goes out with a partial PUT of its row', async () => {
(api.getConfig as jest.Mock).mockResolvedValue({
global: { enabled_providers: ['azure'], default_term: 3, default_payment: 'all-upfront', default_coverage: 80 },
services: [{ provider: 'azure', service: 'compute', term: 3, payment: 'no-upfront', enabled: true, coverage: 80 }],
});
await loadGlobalSettings();
(document.getElementById('azure-vm-term') as HTMLSelectElement).value = '1';
await saveGlobalSettings(new Event('submit'));

expect((api.updateServiceConfig as jest.Mock).mock.calls).toEqual([
['azure', 'compute', { provider: 'azure', service: 'compute', term: 1, payment: 'monthly' }],
]);
});

it('never falls back to upfront when a payment select has no matching option at Save', async () => {
await loadGlobalSettings();
(document.getElementById('azure-vm-payment') as HTMLSelectElement).value = 'junk';
Expand Down
Loading
Loading