Skip to content
Merged
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
73 changes: 44 additions & 29 deletions tests/live.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,21 @@ function required(name) {
return value;
}

// The shared test workspace is rate-limited per minute. A 429 is rejected
// before it is processed, so retrying it is safe (same as the Go and C# suites).
const isRateLimited = (err) => err?.status === 429 || /too many requests/i.test(err?.message ?? '');

async function live(call) {
for (let attempt = 1; ; attempt++) {
try {
return await call();
} catch (err) {
if (!isRateLimited(err) || attempt === 4) throw err;
await new Promise((resolve) => setTimeout(resolve, 10_000));
}
}
}

test('current license and customer workflows work end to end', async () => {
const productId = required('KEYMINT_TEST_PRODUCT_ID');
const baseUrl = process.env.KEYMINT_TEST_BASE_URL || 'https://api.keymint.dev';
Expand All @@ -21,58 +36,58 @@ test('current license and customer workflows work end to end', async () => {
let customerId;

try {
const created = await admin.createKey({
const created = await live(() => admin.createKey({
productId,
maxActivations: '2',
metadata: { purpose: 'node-sdk-live-test', runId }
});
}));
nodeKey = created.key;
assert.ok(nodeKey);

const lookup = await readOnly.getKey({ productId, licenseKey: nodeKey });
const lookup = await live(() => readOnly.getKey({ productId, licenseKey: nodeKey }));
assert.equal(lookup.data.license.productId, productId);

const activation = await client.activateKey({
const activation = await live(() => client.activateKey({
productId, licenseKey: nodeKey, hostId: nodeHost, deviceTag: 'Node SDK CI'
});
}));
assert.equal(activation.metadata.hostId, nodeHost);

const deactivated = await client.deactivateKey({ productId, licenseKey: nodeKey, hostId: nodeHost });
const deactivated = await live(() => client.deactivateKey({ productId, licenseKey: nodeKey, hostId: nodeHost }));
assert.equal(deactivated.devicesRemoved, 1);
await admin.updateKey({ productId, licenseKey: nodeKey, maxActivations: 3 });
await admin.blockKey({ productId, licenseKey: nodeKey });
await admin.unblockKey({ productId, licenseKey: nodeKey });
await live(() => admin.updateKey({ productId, licenseKey: nodeKey, maxActivations: 3 }));
await live(() => admin.blockKey({ productId, licenseKey: nodeKey }));
await live(() => admin.unblockKey({ productId, licenseKey: nodeKey }));

const floating = await admin.createKey({
const floating = await live(() => admin.createKey({
productId,
licenseType: 'floating',
maxConcurrentSessions: 1,
heartbeatInterval: 60,
sessionLeaseDuration: 300,
metadata: { purpose: 'node-sdk-floating-live-test', runId }
});
}));
floatingKey = floating.key;
assert.ok(floatingKey);

const checkout = await client.floatingCheckout({
const checkout = await live(() => client.floatingCheckout({
productId, licenseKey: floatingKey, hostId: `node-floating-${runId}`
});
const heartbeat = await client.floatingHeartbeat({
}));
const heartbeat = await live(() => client.floatingHeartbeat({
productId,
licenseKey: floatingKey,
sessionId: checkout.sessionId,
timestamp: checkout.nextNonce,
signature: KeyMint.generateSessionSignature(checkout.sessionId, checkout.nextNonce, checkout.sessionSecret)
});
await client.floatingCheckin({
}));
await live(() => client.floatingCheckin({
productId,
licenseKey: floatingKey,
sessionId: checkout.sessionId,
timestamp: heartbeat.nextNonce,
signature: KeyMint.generateSessionSignature(checkout.sessionId, heartbeat.nextNonce, checkout.sessionSecret)
});
}));

const signed = await admin.signKey({ productId, licenseKey: nodeKey, hostId: nodeHost, ttl: 300 });
const signed = await live(() => admin.signKey({ productId, licenseKey: nodeKey, hostId: nodeHost, ttl: 300 }));
assert.equal(typeof signed.file, 'string');
assert.match(signed.file, /signedKey/);
const publicKey = process.env.KEYMINT_TEST_PUBLIC_KEY;
Expand All @@ -90,22 +105,22 @@ test('current license and customer workflows work end to end', async () => {
console.log('KEYMINT_TEST_PUBLIC_KEY not set; skipping offline license verification');
}

const customer = await admin.createCustomer({ name: 'Node SDK CI', email: `node-ci-${runId}@example.com` });
const customer = await live(() => admin.createCustomer({ name: 'Node SDK CI', email: `node-ci-${runId}@example.com` }));
customerId = customer.data.id;
assert.ok(customerId);
const fetched = await admin.getCustomerById({ customerId });
const fetched = await live(() => admin.getCustomerById({ customerId }));
assert.ok(fetched.data.some(item => item.id === customerId));
await admin.updateCustomer({ customerId, name: 'Node SDK CI Updated' });
const customers = await admin.getAllCustomers();
await live(() => admin.updateCustomer({ customerId, name: 'Node SDK CI Updated' }));
const customers = await live(() => admin.getAllCustomers());
assert.ok(customers.data.some(item => item.id === customerId));
assert.ok(Array.isArray(await admin.getCustomerWithKeys({ customerId })));
assert.equal((await admin.toggleCustomerStatus({ customerId })).status, true);
assert.equal((await admin.toggleCustomerStatus({ customerId })).status, true);
assert.equal((await admin.deleteCustomer({ customerId })).status, true);
assert.ok(Array.isArray(await live(() => admin.getCustomerWithKeys({ customerId }))));
assert.equal((await live(() => admin.toggleCustomerStatus({ customerId }))).status, true);
assert.equal((await live(() => admin.toggleCustomerStatus({ customerId }))).status, true);
assert.equal((await live(() => admin.deleteCustomer({ customerId }))).status, true);
customerId = undefined;
} finally {
if (customerId) await admin.deleteCustomer({ customerId }).catch(() => {});
if (nodeKey) await admin.blockKey({ productId, licenseKey: nodeKey }).catch(() => {});
if (floatingKey) await admin.blockKey({ productId, licenseKey: floatingKey }).catch(() => {});
if (customerId) await live(() => admin.deleteCustomer({ customerId })).catch(() => {});
if (nodeKey) await live(() => admin.blockKey({ productId, licenseKey: nodeKey })).catch(() => {});
if (floatingKey) await live(() => admin.blockKey({ productId, licenseKey: floatingKey })).catch(() => {});
}
});
Loading