186 lines
5.5 KiB
TypeScript
186 lines
5.5 KiB
TypeScript
import { strict as assert } from 'node:assert';
|
|
import { describe, it } from 'node:test';
|
|
import type { Allocation, Database } from '@pig/db';
|
|
import type { Principal } from '../src/lib/auth';
|
|
import { executeMutation } from '../src/lib/mutation';
|
|
import { createAllocationMutationDefinition } from '../src/routes/capacity-writes';
|
|
import {
|
|
findCapacityViolation,
|
|
type CommitmentCapacity,
|
|
type ReservationCapacity,
|
|
} from '../src/services/capacity-writes';
|
|
|
|
const HOUR = 3_600_000;
|
|
const START = new Date('2026-01-01T00:00:00.000Z');
|
|
|
|
function at(hour: number): Date {
|
|
return new Date(START.getTime() + hour * HOUR);
|
|
}
|
|
|
|
function commitment(overrides: Partial<CommitmentCapacity> = {}): CommitmentCapacity {
|
|
return {
|
|
id: '10000000-0000-4000-8000-000000000001',
|
|
gpuCount: 8,
|
|
startsAt: at(0),
|
|
endsAt: at(10),
|
|
totalGpuHours: 80,
|
|
shape: null,
|
|
oversubscriptionPct: 0,
|
|
terminatedAt: null,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function reservation(overrides: Partial<ReservationCapacity> = {}): ReservationCapacity {
|
|
return {
|
|
gpuHours: 80,
|
|
startsAt: at(0),
|
|
endsAt: at(10),
|
|
status: 'committed',
|
|
holdExpiresAt: null,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe('allocation availability invariant', () => {
|
|
it('permits only the oversubscription explicitly recorded on the commitment', () => {
|
|
const existing = reservation();
|
|
const extra = reservation({ gpuHours: 20 });
|
|
|
|
assert.equal(
|
|
findCapacityViolation(
|
|
commitment({ totalGpuHours: 100, oversubscriptionPct: 25 }),
|
|
[existing],
|
|
extra,
|
|
at(-1),
|
|
),
|
|
null,
|
|
);
|
|
assert.equal(
|
|
findCapacityViolation(commitment({ totalGpuHours: 100 }), [existing], extra, at(-1))?.code,
|
|
'shape_capacity_exceeded',
|
|
);
|
|
});
|
|
|
|
it('ignores expired holds but live holds still reserve capacity', () => {
|
|
const expired = reservation({
|
|
status: 'planned',
|
|
holdExpiresAt: at(-1),
|
|
});
|
|
const live = reservation({
|
|
status: 'planned',
|
|
holdExpiresAt: at(1),
|
|
});
|
|
const requested = reservation();
|
|
|
|
assert.equal(findCapacityViolation(commitment(), [expired], requested, at(0)), null);
|
|
assert.equal(
|
|
findCapacityViolation(commitment(), [live], requested, at(0))?.code,
|
|
'total_capacity_exceeded',
|
|
);
|
|
});
|
|
|
|
it('checks each authoritative shape interval instead of averaging the term', () => {
|
|
const shaped = commitment({
|
|
endsAt: at(20),
|
|
totalGpuHours: 120,
|
|
shape: {
|
|
intervals: [at(0).toISOString(), at(10).toISOString(), at(20).toISOString()],
|
|
quantities: [8, 4],
|
|
},
|
|
});
|
|
const firstTranche = reservation({ gpuHours: 60 });
|
|
const overlapsRamp = reservation({ gpuHours: 30 });
|
|
const fitsLaterTranche = reservation({
|
|
gpuHours: 40,
|
|
startsAt: at(10),
|
|
endsAt: at(20),
|
|
});
|
|
|
|
assert.equal(
|
|
findCapacityViolation(shaped, [firstTranche], overlapsRamp, at(-1))?.code,
|
|
'shape_capacity_exceeded',
|
|
);
|
|
assert.equal(findCapacityViolation(shaped, [firstTranche], fitsLaterTranche, at(-1)), null);
|
|
});
|
|
});
|
|
|
|
const principal: Principal = {
|
|
userId: '00000000-0000-4000-8000-000000000001',
|
|
email: 'seller@example.com',
|
|
name: 'Seller',
|
|
isPlatformAdmin: false,
|
|
teams: [{ team: 'demand', role: 'member' }],
|
|
via: 'jwt',
|
|
scopes: ['read', 'write'],
|
|
};
|
|
|
|
describe('allocation mutation transaction', () => {
|
|
it('passes the mutation transaction through the capacity check and audit write', async () => {
|
|
const events: string[] = [];
|
|
const tx = {
|
|
insert: () => ({
|
|
values: async () => {
|
|
events.push('activity');
|
|
},
|
|
}),
|
|
};
|
|
const db = {
|
|
transaction: async (work: (transaction: unknown) => Promise<unknown>) => {
|
|
events.push('begin');
|
|
const result = await work(tx);
|
|
events.push('commit');
|
|
return result;
|
|
},
|
|
} as unknown as Database;
|
|
|
|
const definition = createAllocationMutationDefinition((transaction) => {
|
|
assert.equal(transaction, tx);
|
|
return {
|
|
createAllocation: async (input) => {
|
|
events.push('lock-check-insert');
|
|
return {
|
|
allocation: {
|
|
id: '30000000-0000-4000-8000-000000000003',
|
|
capacityCommitmentId: input.capacityCommitmentId,
|
|
demandDealId: input.demandDealId,
|
|
gpuHours: String(input.gpuHours),
|
|
pricePerGpuHourCents: input.pricePerGpuHourCents,
|
|
status: input.status,
|
|
} as Allocation,
|
|
commitment: {
|
|
id: input.capacityCommitmentId,
|
|
name: 'Eight H100s',
|
|
},
|
|
deal: {
|
|
id: input.demandDealId,
|
|
accountId: '40000000-0000-4000-8000-000000000004',
|
|
},
|
|
};
|
|
},
|
|
createCommitment: async () => assert.fail('wrong mutation'),
|
|
updateCommitment: async () => assert.fail('wrong mutation'),
|
|
createHold: async () => assert.fail('wrong mutation'),
|
|
releaseAllocation: async () => assert.fail('wrong mutation'),
|
|
};
|
|
});
|
|
|
|
await executeMutation(
|
|
db,
|
|
principal,
|
|
async () => ({
|
|
capacityCommitmentId: '10000000-0000-4000-8000-000000000001',
|
|
demandDealId: '20000000-0000-4000-8000-000000000002',
|
|
gpuHours: 8,
|
|
pricePerGpuHourCents: 225,
|
|
startsAt: '2026-01-01T00:00:00.000Z',
|
|
endsAt: '2026-01-01T01:00:00.000Z',
|
|
status: 'committed',
|
|
}),
|
|
definition,
|
|
);
|
|
|
|
assert.deepEqual(events, ['begin', 'lock-check-insert', 'activity', 'commit']);
|
|
});
|
|
});
|