Files
puter/src/backend/drivers/meta.test.ts

653 lines
23 KiB
TypeScript

/*
* Copyright (C) 2024-present Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { describe, expect, it } from 'vitest';
import { Driver } from './decorators.js';
import {
resolveDriverMeta,
resolveDriverMethodConcurrent,
resolveDriverMethodRateLimit,
resolveDriverMethodRequireReputation,
resolveDriverMethodRequireSubscription,
validateDriverConcurrent,
validateDriverRateLimit,
validateDriverRequireReputation,
validateDriverRequireSubscription,
type DriverConcurrentConfig,
type DriverRateLimitConfig,
type DriverRequireReputationConfig,
type DriverRequireSubscriptionConfig,
} from './meta.js';
// ── validateDriverRateLimit ─────────────────────────────────────────
describe('validateDriverRateLimit', () => {
it('returns an empty config for null/undefined input', () => {
expect(validateDriverRateLimit(undefined, 't')).toEqual({});
expect(validateDriverRateLimit(null, 't')).toEqual({});
});
it('accepts a well-formed default + methods block', () => {
const cfg: DriverRateLimitConfig = {
default: { limit: 600, window: 60_000 },
methods: {
get: { limit: 1000, window: 60_000, backend: 'kv' },
set: { limit: 100, window: 60_000, backend: 'redis' },
},
};
expect(validateDriverRateLimit(cfg, 't')).toBe(cfg);
});
it('rejects a non-object config', () => {
expect(() => validateDriverRateLimit(42, 't')).toThrow(
/rateLimit must be an object/,
);
expect(() => validateDriverRateLimit([], 't')).toThrow(
/rateLimit must be an object/,
);
});
it('rejects non-positive / non-numeric limit and window', () => {
expect(() =>
validateDriverRateLimit({ default: { limit: 0, window: 1 } }, 't'),
).toThrow(/limit: expected a positive number/);
expect(() =>
validateDriverRateLimit(
{ default: { limit: 1, window: -10 } },
't',
),
).toThrow(/window: expected a positive number/);
expect(() =>
validateDriverRateLimit(
{ default: { limit: 'x', window: 60_000 } },
't',
),
).toThrow(/limit: expected a positive number/);
});
it('rejects unknown backend names', () => {
expect(() =>
validateDriverRateLimit(
{
default: {
limit: 1,
window: 1_000,
backend: 'sqlite',
},
},
't',
),
).toThrow(/backend: expected one of/);
});
it('walks the methods map and labels the failing entry', () => {
expect(() =>
validateDriverRateLimit(
{
methods: {
goodOne: { limit: 5, window: 60_000 },
badOne: { limit: 1 },
},
},
'drv',
),
).toThrow(/drv\.rateLimit\.methods\.badOne\.window/);
});
it('rejects a non-object methods bag', () => {
expect(() =>
validateDriverRateLimit({ methods: [] as unknown }, 't'),
).toThrow(/methods must be an object/);
});
it('accepts bySubscription on a rate-limit spec', () => {
const cfg: DriverRateLimitConfig = {
methods: {
chat: {
limit: 10,
window: 60_000,
bySubscription: { user_free: 2, unlimited: 1000 },
},
},
};
expect(validateDriverRateLimit(cfg, 't')).toBe(cfg);
});
it('rejects malformed bySubscription entries on a rate-limit spec', () => {
// Symmetry with the concurrent validator — bad numbers fail loud.
expect(() =>
validateDriverRateLimit(
{
default: {
limit: 1,
window: 1_000,
bySubscription: { user_free: -3 },
},
},
'drv',
),
).toThrow(/drv\.rateLimit\.default\.bySubscription\.user_free/);
});
});
// ── resolveDriverMethodRateLimit ────────────────────────────────────
describe('resolveDriverMethodRateLimit', () => {
const cfg: DriverRateLimitConfig = {
default: { limit: 100, window: 60_000 },
methods: {
get: { limit: 1000, window: 60_000, backend: 'memory' },
},
};
it('returns the per-method spec when one is declared', () => {
expect(resolveDriverMethodRateLimit(cfg, 'get')).toEqual({
limit: 1000,
window: 60_000,
backend: 'memory',
});
});
it('falls back to the default spec when no method override exists', () => {
expect(resolveDriverMethodRateLimit(cfg, 'set')).toEqual({
limit: 100,
window: 60_000,
});
});
it('returns undefined when the driver declared no rate-limit at all', () => {
expect(resolveDriverMethodRateLimit(undefined, 'get')).toBeUndefined();
});
it('returns undefined when neither default nor a matching method is declared', () => {
expect(
resolveDriverMethodRateLimit(
{ methods: { other: { limit: 1, window: 1 } } },
'get',
),
).toBeUndefined();
});
});
// ── @Driver decorator: rateLimit propagation ────────────────────────
describe('@Driver — rateLimit option', () => {
it('stamps a validated rateLimit block onto the prototype, surfacing via resolveDriverMeta', () => {
@Driver('test-iface', {
name: 'test-impl',
rateLimit: {
default: { limit: 50, window: 60_000 },
methods: {
chat: { limit: 10, window: 60_000, backend: 'redis' },
},
},
})
class FakeDriver {}
const inst = new FakeDriver();
const meta = resolveDriverMeta(
inst as unknown as Record<string, unknown> & {
onServerStart?: () => void;
onServerPrepareShutdown?: () => void;
onServerShutdown?: () => void;
},
);
expect(meta).not.toBeNull();
expect(meta?.rateLimit).toEqual({
default: { limit: 50, window: 60_000 },
methods: {
chat: { limit: 10, window: 60_000, backend: 'redis' },
},
});
});
it('throws at decoration time on a malformed rateLimit block', () => {
// The whole point of eager validation: bad config takes the
// module down at boot, not at the first request.
expect(() => {
@Driver('test-iface', {
name: 'broken',
rateLimit: { default: { limit: -1, window: 1_000 } },
})
class BrokenDriver {}
void BrokenDriver;
}).toThrow(/limit: expected a positive number/);
});
it('falls back to imperative `rateLimit` field when no decorator metadata is set', () => {
// Imperative drivers (no decorator) declare the field directly.
class Imperative {
readonly driverInterface = 'imp-iface';
readonly driverName = 'imp';
readonly rateLimit = {
methods: { foo: { limit: 7, window: 1_000 } },
};
}
const inst = new Imperative();
const meta = resolveDriverMeta(
inst as unknown as Record<string, unknown> & {
onServerStart?: () => void;
onServerPrepareShutdown?: () => void;
onServerShutdown?: () => void;
},
);
expect(meta?.rateLimit?.methods?.foo).toEqual({
limit: 7,
window: 1_000,
});
});
it('validates the imperative `rateLimit` field on first read (loud failure)', () => {
class BadImperative {
readonly driverInterface = 'imp-iface';
readonly driverName = 'imp-bad';
// Invalid: backend not one of memory/redis/kv.
readonly rateLimit = {
default: { limit: 1, window: 1_000, backend: 'mysql' },
};
}
expect(() =>
resolveDriverMeta(
new BadImperative() as unknown as Record<string, unknown> & {
onServerStart?: () => void;
onServerPrepareShutdown?: () => void;
onServerShutdown?: () => void;
},
),
).toThrow(/backend: expected one of/);
});
});
// ── validateDriverConcurrent ────────────────────────────────────────
describe('validateDriverConcurrent', () => {
it('returns an empty config for null/undefined input', () => {
expect(validateDriverConcurrent(undefined, 't')).toEqual({});
expect(validateDriverConcurrent(null, 't')).toEqual({});
});
it('accepts a well-formed default + methods block with bySubscription', () => {
const cfg: DriverConcurrentConfig = {
default: { limit: 5 },
methods: {
heavy: {
limit: 5,
bySubscription: { user_free: 1, unlimited: 50 },
backend: 'redis',
},
},
};
expect(validateDriverConcurrent(cfg, 't')).toBe(cfg);
});
it('rejects non-positive / non-numeric limit', () => {
expect(() =>
validateDriverConcurrent({ default: { limit: 0 } }, 't'),
).toThrow(/limit: expected a positive number/);
expect(() =>
validateDriverConcurrent({ default: { limit: 'x' } }, 't'),
).toThrow(/limit: expected a positive number/);
});
it('rejects unknown backend names', () => {
expect(() =>
validateDriverConcurrent(
{ default: { limit: 1, backend: 'sqlite' } },
't',
),
).toThrow(/backend: expected one of/);
});
it('rejects malformed bySubscription entries with a labelled path', () => {
expect(() =>
validateDriverConcurrent(
{
default: {
limit: 5,
bySubscription: { user_free: -1 },
},
},
'drv',
),
).toThrow(/drv\.concurrent\.default\.bySubscription\.user_free/);
});
it('walks the methods map and labels the failing entry', () => {
expect(() =>
validateDriverConcurrent(
{
methods: {
goodOne: { limit: 5 },
badOne: { limit: 1, backend: 'sqlite' },
},
},
'drv',
),
).toThrow(/drv\.concurrent\.methods\.badOne\.backend/);
});
});
// ── resolveDriverMethodConcurrent ───────────────────────────────────
describe('resolveDriverMethodConcurrent', () => {
const cfg: DriverConcurrentConfig = {
default: { limit: 3 },
methods: {
heavy: { limit: 1, backend: 'redis' },
},
};
it('returns the per-method spec when one is declared', () => {
expect(resolveDriverMethodConcurrent(cfg, 'heavy')).toEqual({
limit: 1,
backend: 'redis',
});
});
it('falls back to default for methods not in the map', () => {
expect(resolveDriverMethodConcurrent(cfg, 'light')).toEqual({
limit: 3,
});
});
it('returns undefined when nothing is declared', () => {
expect(
resolveDriverMethodConcurrent(undefined, 'anything'),
).toBeUndefined();
expect(resolveDriverMethodConcurrent({}, 'anything')).toBeUndefined();
});
});
// ── @Driver — concurrent option ─────────────────────────────────────
describe('@Driver — concurrent option', () => {
it('stamps a validated concurrent block onto the prototype', () => {
@Driver('test-iface', {
name: 'cdec',
concurrent: {
default: { limit: 4 },
methods: {
chat: {
limit: 5,
bySubscription: { user_free: 1 },
backend: 'redis',
},
},
},
})
class FakeDriver {}
const inst = new FakeDriver();
const meta = resolveDriverMeta(
inst as unknown as Record<string, unknown> & {
onServerStart?: () => void;
onServerPrepareShutdown?: () => void;
onServerShutdown?: () => void;
},
);
expect(meta?.concurrent).toEqual({
default: { limit: 4 },
methods: {
chat: {
limit: 5,
bySubscription: { user_free: 1 },
backend: 'redis',
},
},
});
});
it('throws at decoration time on a malformed concurrent block', () => {
expect(() => {
@Driver('test-iface', {
name: 'bad-concurrent',
concurrent: { default: { limit: -1 } },
})
class BrokenDriver {}
void BrokenDriver;
}).toThrow(/limit: expected a positive number/);
});
it('falls back to imperative `concurrent` field when decorator metadata is absent', () => {
class Imperative {
readonly driverInterface = 'imp-iface';
readonly driverName = 'imp-c';
readonly concurrent = {
methods: { foo: { limit: 2 } },
};
}
const meta = resolveDriverMeta(
new Imperative() as unknown as Record<string, unknown> & {
onServerStart?: () => void;
onServerPrepareShutdown?: () => void;
onServerShutdown?: () => void;
},
);
expect(meta?.concurrent?.methods?.foo).toEqual({ limit: 2 });
});
});
// ── requireSubscription ─────────────────────────────────────────────
describe('validateDriverRequireSubscription', () => {
it('returns an empty config for null/undefined input', () => {
expect(validateDriverRequireSubscription(undefined, 't')).toEqual({});
expect(validateDriverRequireSubscription(null, 't')).toEqual({});
});
it('accepts booleans and id allowlists', () => {
const cfg: DriverRequireSubscriptionConfig = {
default: false,
methods: { generate: true, generateLong: ['business', 'pro'] },
};
expect(validateDriverRequireSubscription(cfg, 't')).toBe(cfg);
});
it('rejects a non-object config', () => {
expect(() => validateDriverRequireSubscription(42, 't')).toThrow(
/requireSubscription must be an object/,
);
expect(() => validateDriverRequireSubscription([], 't')).toThrow(
/requireSubscription must be an object/,
);
});
it('rejects requirements that name nothing', () => {
expect(() =>
validateDriverRequireSubscription({ default: [] }, 't'),
).toThrow(/at least one subscription id/);
expect(() =>
validateDriverRequireSubscription({ methods: { a: [1] } }, 't'),
).toThrow(/must be strings/);
expect(() =>
validateDriverRequireSubscription({ methods: { a: 'pro' } }, 't'),
).toThrow(/expected true\/false or an array of ids/);
});
});
describe('resolveDriverMethodRequireSubscription', () => {
const cfg: DriverRequireSubscriptionConfig = {
default: true,
methods: { list: false, generateLong: ['pro'] },
};
it('prefers a per-method entry over the default', () => {
expect(resolveDriverMethodRequireSubscription(cfg, 'list')).toBe(false);
expect(
resolveDriverMethodRequireSubscription(cfg, 'generateLong'),
).toEqual(['pro']);
});
it('falls back to the default, and to undefined with no config', () => {
expect(resolveDriverMethodRequireSubscription(cfg, 'generate')).toBe(
true,
);
expect(
resolveDriverMethodRequireSubscription(undefined, 'generate'),
).toBeUndefined();
expect(resolveDriverMethodRequireSubscription({}, 'generate')).toBe(
undefined,
);
});
});
describe('resolveDriverMeta — requireSubscription', () => {
it('reads the block off the decorator', () => {
@Driver('test-iface', {
name: 'decorated',
requireSubscription: { methods: { generate: true } },
})
class Decorated {}
expect(resolveDriverMeta(new Decorated() as never)).toMatchObject({
requireSubscription: { methods: { generate: true } },
});
});
it('validates an imperatively declared block', () => {
const driver = {
driverInterface: 'test-iface',
driverName: 'imperative',
requireSubscription: { default: ['pro'] },
};
expect(resolveDriverMeta(driver as never)).toMatchObject({
requireSubscription: { default: ['pro'] },
});
const bad = { ...driver, requireSubscription: { default: 'pro' } };
expect(() => resolveDriverMeta(bad as never)).toThrow(
/expected true\/false or an array of ids/,
);
});
it('leaves the block absent when a driver declares nothing', () => {
const driver = {
driverInterface: 'test-iface',
driverName: 'plain',
};
expect(
resolveDriverMeta(driver as never)?.requireSubscription,
).toBeUndefined();
});
});
// ── requireReputation ───────────────────────────────────────────────
describe('validateDriverRequireReputation', () => {
it('returns an empty config for null/undefined input', () => {
expect(validateDriverRequireReputation(undefined, 't')).toEqual({});
expect(validateDriverRequireReputation(null, 't')).toEqual({});
});
it('accepts tier names and explicit opt-outs', () => {
const cfg: DriverRequireReputationConfig = {
default: false,
methods: { generate: 'standard' },
};
expect(validateDriverRequireReputation(cfg, 't')).toBe(cfg);
});
it('rejects a non-object config', () => {
expect(() => validateDriverRequireReputation(42, 't')).toThrow(
/requireReputation must be an object/,
);
expect(() => validateDriverRequireReputation([], 't')).toThrow(
/requireReputation must be an object/,
);
});
it('rejects requirements that name no tier', () => {
expect(() =>
validateDriverRequireReputation({ default: '' }, 't'),
).toThrow(/non-empty tier name/);
expect(() =>
validateDriverRequireReputation({ methods: { a: true } }, 't'),
).toThrow(/expected a tier name, or false/);
expect(() =>
validateDriverRequireReputation({ methods: { a: 60 } }, 't'),
).toThrow(/expected a tier name, or false/);
});
});
describe('resolveDriverMethodRequireReputation', () => {
const cfg: DriverRequireReputationConfig = {
default: 'standard',
methods: { list: false, generateLong: 'trusted' },
};
it('prefers a per-method entry over the default', () => {
expect(resolveDriverMethodRequireReputation(cfg, 'list')).toBe(false);
expect(resolveDriverMethodRequireReputation(cfg, 'generateLong')).toBe(
'trusted',
);
});
it('falls back to the default, and to undefined with no config', () => {
expect(resolveDriverMethodRequireReputation(cfg, 'generate')).toBe(
'standard',
);
expect(
resolveDriverMethodRequireReputation(undefined, 'generate'),
).toBeUndefined();
expect(
resolveDriverMethodRequireReputation({}, 'generate'),
).toBeUndefined();
});
});
describe('resolveDriverMeta — requireReputation', () => {
it('reads the block off the decorator', () => {
@Driver('test-iface', {
name: 'decorated-reputation',
requireReputation: { methods: { generate: 'standard' } },
})
class Decorated {}
expect(resolveDriverMeta(new Decorated() as never)).toMatchObject({
requireReputation: { methods: { generate: 'standard' } },
});
});
it('validates an imperatively declared block', () => {
const driver = {
driverInterface: 'test-iface',
driverName: 'imperative',
requireReputation: { default: 'standard' },
};
expect(resolveDriverMeta(driver as never)).toMatchObject({
requireReputation: { default: 'standard' },
});
const bad = { ...driver, requireReputation: { default: 60 } };
expect(() => resolveDriverMeta(bad as never)).toThrow(
/expected a tier name, or false/,
);
});
it('leaves the block absent when a driver declares nothing', () => {
const driver = {
driverInterface: 'test-iface',
driverName: 'plain',
};
expect(
resolveDriverMeta(driver as never)?.requireReputation,
).toBeUndefined();
});
});