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
78 changes: 78 additions & 0 deletions frontend/src/__tests__/settings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -641,4 +641,82 @@ describe('Settings Module', () => {
jest.useRealTimers();
});
});

describe('Azure payment defaults (issue #12)', () => {
// Shared fixture covers term selects only; inject the four Azure payment
// selects this issue introduces so loadGlobalSettings / saveGlobalSettings
// have DOM nodes to read / write.
const injectAzurePaymentSelects = () => {
const form = document.getElementById('global-settings-form');
if (!form) throw new Error('global-settings-form fixture missing');
for (const svc of ['vm', 'sql', 'cosmosdb', 'redis']) {
if (document.getElementById(`azure-${svc}-payment`)) continue;
const select = document.createElement('select');
select.id = `azure-${svc}-payment`;
for (const [value, label] of [['all-upfront', 'Upfront'], ['no-upfront', 'Monthly']] as const) {
const opt = document.createElement('option');
opt.value = value;
opt.textContent = label;
if (value === 'all-upfront') opt.selected = true;
select.appendChild(opt);
}
form.appendChild(select);
}
};

test('loadGlobalSettings applies persisted azure-vm payment', async () => {
injectAzurePaymentSelects();
(api.getConfig as jest.Mock).mockResolvedValue({
global: {
enabled_providers: ['aws', 'azure'],
default_term: 3,
default_payment: 'all-upfront',
default_coverage: 80,
},
services: [
{ provider: 'azure', service: 'vm', term: 3, payment: 'no-upfront', enabled: true, coverage: 80 },
],
});

await loadGlobalSettings();

const vmPayment = document.getElementById('azure-vm-payment') as HTMLSelectElement;
expect(vmPayment.value).toBe('no-upfront');
});

test('saveGlobalSettings sends the per-service Azure payment, not the global default', async () => {
injectAzurePaymentSelects();
(api.getConfig as jest.Mock).mockResolvedValue({
global: { enabled_providers: ['aws', 'azure'], default_term: 3, default_payment: 'all-upfront', default_coverage: 80 },
services: [],
});
(api.updateConfig as jest.Mock).mockResolvedValue({});
(api.updateServiceConfig as jest.Mock).mockClear().mockResolvedValue(undefined);

await loadGlobalSettings();
// User flips Azure VM to Monthly while global default stays all-upfront.
(document.getElementById('azure-vm-payment') as HTMLSelectElement).value = 'no-upfront';
(document.getElementById('setting-default-payment') as HTMLSelectElement).value = 'all-upfront';

await saveGlobalSettings({ preventDefault: jest.fn() } as unknown as Event);

const azureVmCall = (api.updateServiceConfig as jest.Mock).mock.calls.find(
([provider, service]) => provider === 'azure' && service === 'vm',
);
expect(azureVmCall).toBeDefined();
const cfg = azureVmCall![2];
expect(cfg.payment).toBe('no-upfront');
});

test('help text describes the correct per-provider payment semantics', () => {
// The help copy lives in index.html (loaded into the test fixture
// verbatim at test startup). Guard against regressions in the
// wording — the old copy falsely claimed Azure was always upfront.
const fs = require('fs') as typeof import('fs');
const path = require('path') as typeof import('path');
const html = fs.readFileSync(path.join(__dirname, '..', 'index.html'), 'utf-8');
expect(html).toMatch(/Azure reservations support upfront or monthly/);
expect(html).not.toMatch(/Azure and GCP reservations are always paid upfront/);
});
});
});
6 changes: 5 additions & 1 deletion frontend/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ <h2>Purchasing Settings</h2>

<fieldset class="settings-category" id="purchasing-global-defaults">
<legend>Global Purchase Defaults</legend>
<p class="settings-help">Changing these defaults will automatically update all compatible service settings below. Payment options only apply to AWS services; Azure and GCP reservations are always paid upfront.</p>
<p class="settings-help">Changing these defaults will automatically update all compatible service settings below. Payment options map differently per provider: AWS uses no/partial/all upfront; Azure reservations support upfront or monthly; GCP commitments are billed monthly only.</p>
<div class="setting-row">
<div class="setting-info">
<label for="setting-default-term">Default Term</label>
Expand Down Expand Up @@ -499,18 +499,22 @@ <h4 class="subsection-title">Service Defaults</h4>
<div class="service-default-card">
<h5>Virtual Machine Reservations</h5>
<label>Term: <select id="azure-vm-term"><option value="1">1 Year</option><option value="3" selected>3 Years</option></select></label>
<label>Payment: <select id="azure-vm-payment"><option value="all-upfront" selected>Upfront</option><option value="no-upfront">Monthly</option></select></label>
</div>
<div class="service-default-card">
<h5>SQL Database Reservations</h5>
<label>Term: <select id="azure-sql-term"><option value="1">1 Year</option><option value="3" selected>3 Years</option></select></label>
<label>Payment: <select id="azure-sql-payment"><option value="all-upfront" selected>Upfront</option><option value="no-upfront">Monthly</option></select></label>
</div>
<div class="service-default-card">
<h5>CosmosDB Reservations</h5>
<label>Term: <select id="azure-cosmosdb-term"><option value="1">1 Year</option><option value="3" selected>3 Years</option></select></label>
<label>Payment: <select id="azure-cosmosdb-payment"><option value="all-upfront" selected>Upfront</option><option value="no-upfront">Monthly</option></select></label>
</div>
<div class="service-default-card">
<h5>Redis Cache Reservations</h5>
<label>Term: <select id="azure-redis-term"><option value="1">1 Year</option><option value="3" selected>3 Years</option></select></label>
<label>Payment: <select id="azure-redis-payment"><option value="all-upfront" selected>Upfront</option><option value="no-upfront">Monthly</option></select></label>
</div>
</div>
</fieldset>
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ const SERVICE_FIELDS = [
{ provider: 'aws', service: 'opensearch', termId: 'aws-opensearch-term', paymentId: 'aws-opensearch-payment' },
{ provider: 'aws', service: 'redshift', termId: 'aws-redshift-term', paymentId: 'aws-redshift-payment' },
{ provider: 'aws', service: 'savingsplans', termId: 'aws-savingsplans-term', paymentId: 'aws-savingsplans-payment' },
{ provider: 'azure', service: 'vm', termId: 'azure-vm-term', paymentId: null },
{ provider: 'azure', service: 'sql', termId: 'azure-sql-term', paymentId: null },
{ provider: 'azure', service: 'cosmosdb', termId: 'azure-cosmosdb-term', paymentId: null },
{ provider: 'azure', service: 'redis', termId: 'azure-redis-term', paymentId: null },
{ provider: 'azure', service: 'vm', termId: 'azure-vm-term', paymentId: 'azure-vm-payment' },
{ provider: 'azure', service: 'sql', termId: 'azure-sql-term', paymentId: 'azure-sql-payment' },
{ provider: 'azure', service: 'cosmosdb', termId: 'azure-cosmosdb-term', paymentId: 'azure-cosmosdb-payment' },
{ provider: 'azure', service: 'redis', termId: 'azure-redis-term', paymentId: 'azure-redis-payment' },
{ provider: 'gcp', service: 'compute', termId: 'gcp-compute-term', paymentId: null },
{ provider: 'gcp', service: 'sql', termId: 'gcp-sql-term', paymentId: null },
{ provider: 'gcp', service: 'memorystore',termId: 'gcp-memorystore-term', paymentId: null },
Expand Down