Add self-serve billing: tiers, credits, BTCPay and Zaprite
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
// Node-built-in test runner. Run with `node --test server/test/`.
|
||||
//
|
||||
// Targets the credit-key resolver added in the license-keyed-credits
|
||||
// refactor. The headline guarantee: same license + different
|
||||
// install_ids resolve to the SAME credit key. Plus a handful of
|
||||
// adjacent cases worth pinning so the contract doesn't drift.
|
||||
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { getCreditKey, licenseFingerprint } from "../credits.js";
|
||||
|
||||
test("licenseFingerprint: stable hash from licenseUuid", () => {
|
||||
const fp1 = licenseFingerprint({
|
||||
tier: "pro",
|
||||
licenseUuid: "11111111-2222-3333-4444-555555555555",
|
||||
});
|
||||
const fp2 = licenseFingerprint({
|
||||
tier: "pro",
|
||||
licenseUuid: "11111111-2222-3333-4444-555555555555",
|
||||
});
|
||||
assert.equal(typeof fp1, "string");
|
||||
assert.equal(fp1.length, 16);
|
||||
assert.equal(fp1, fp2, "same licenseUuid should yield same fingerprint");
|
||||
});
|
||||
|
||||
test("licenseFingerprint: different UUIDs yield different fingerprints", () => {
|
||||
const fp1 = licenseFingerprint({
|
||||
tier: "pro",
|
||||
licenseUuid: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
|
||||
});
|
||||
const fp2 = licenseFingerprint({
|
||||
tier: "pro",
|
||||
licenseUuid: "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
|
||||
});
|
||||
assert.notEqual(fp1, fp2);
|
||||
});
|
||||
|
||||
test("licenseFingerprint: missing licenseUuid returns null", () => {
|
||||
assert.equal(licenseFingerprint(null), null);
|
||||
assert.equal(licenseFingerprint({ tier: "pro" }), null);
|
||||
assert.equal(licenseFingerprint({ tier: "pro", licenseUuid: null }), null);
|
||||
});
|
||||
|
||||
test("getCreditKey: Core falls back to inst:<installId>", () => {
|
||||
const k = getCreditKey({
|
||||
installId: "abc-123",
|
||||
license: { tier: "core" },
|
||||
});
|
||||
assert.equal(k, "inst:abc-123");
|
||||
});
|
||||
|
||||
test("getCreditKey: anonymous (no license) uses inst:<installId>", () => {
|
||||
assert.equal(getCreditKey({ installId: "xyz" }), "inst:xyz");
|
||||
assert.equal(getCreditKey({ installId: "xyz", license: null }), "inst:xyz");
|
||||
});
|
||||
|
||||
test("getCreditKey: Pro license routes to lic:<fingerprint>", () => {
|
||||
const k = getCreditKey({
|
||||
installId: "any-install",
|
||||
license: {
|
||||
tier: "pro",
|
||||
licenseUuid: "11111111-2222-3333-4444-555555555555",
|
||||
},
|
||||
});
|
||||
assert.match(k, /^lic:[0-9a-f]{16}$/);
|
||||
});
|
||||
|
||||
// Headline guarantee for the refactor: one license activated on two
|
||||
// different installs MUST resolve to the same credit-key, so both
|
||||
// devices share one Pro monthly pool.
|
||||
test("getCreditKey: same license + different installs → same key", () => {
|
||||
const license = {
|
||||
tier: "pro",
|
||||
licenseUuid: "11111111-2222-3333-4444-555555555555",
|
||||
};
|
||||
const k1 = getCreditKey({ installId: "install-A", license });
|
||||
const k2 = getCreditKey({ installId: "install-B", license });
|
||||
assert.equal(k1, k2);
|
||||
assert.match(k1, /^lic:/);
|
||||
});
|
||||
|
||||
test("getCreditKey: same install + different licenses → different keys", () => {
|
||||
const installId = "shared-install";
|
||||
const k1 = getCreditKey({
|
||||
installId,
|
||||
license: {
|
||||
tier: "pro",
|
||||
licenseUuid: "11111111-2222-3333-4444-555555555555",
|
||||
},
|
||||
});
|
||||
const k2 = getCreditKey({
|
||||
installId,
|
||||
license: {
|
||||
tier: "pro",
|
||||
licenseUuid: "99999999-9999-9999-9999-999999999999",
|
||||
},
|
||||
});
|
||||
assert.notEqual(k1, k2);
|
||||
});
|
||||
|
||||
test("getCreditKey: Max tier also routes to lic:<fingerprint>", () => {
|
||||
const k = getCreditKey({
|
||||
installId: "any-install",
|
||||
license: {
|
||||
tier: "max",
|
||||
licenseUuid: "11111111-2222-3333-4444-555555555555",
|
||||
},
|
||||
});
|
||||
assert.match(k, /^lic:[0-9a-f]{16}$/);
|
||||
});
|
||||
|
||||
// Paid tier with no resolvable fingerprint (license object missing
|
||||
// licenseUuid) should defensively fall back to install-keyed rather
|
||||
// than throwing or producing a phantom "lic:" key — keeps the ledger
|
||||
// behaving correctly when the keysat verifier returns degraded data.
|
||||
test("getCreditKey: paid tier without licenseUuid falls back to inst:", () => {
|
||||
const k = getCreditKey({
|
||||
installId: "ins-77",
|
||||
license: { tier: "pro" }, // no licenseUuid
|
||||
});
|
||||
assert.equal(k, "inst:ins-77");
|
||||
});
|
||||
|
||||
test("getCreditKey: throws when neither installId nor a usable license is present", () => {
|
||||
assert.throws(
|
||||
() => getCreditKey({}),
|
||||
/installId required/i,
|
||||
"should throw on empty input"
|
||||
);
|
||||
assert.throws(
|
||||
() => getCreditKey({ license: { tier: "core" } }),
|
||||
/installId required/i,
|
||||
"should throw on Core license with no installId"
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user