import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts' import * as TestApp from '../../../../test/App.js' import * as Faucet from './faucet.js' import * as Orgs from './orgs.js' const origin = 'http://localhost' function createApp() { return TestApp.create({ rateLimit: { now: () => new Date('2026-01-01T00:00:00.000Z') }, session: { wallet: { origin } }, }) } async function session(app: TestApp.signIn.App) { const account = privateKeyToAccount(generatePrivateKey()) const { cookie } = await TestApp.signIn(app, account) return cookie! } async function organization(app: ReturnType, cookie: string) { const response = await app.request('/v1/orgs', { body: JSON.stringify({ name: 'Acme' }), headers: { 'content-type': 'application/json', cookie }, method: 'POST', }) return TestApp.json(response, Orgs.schema.Organization) } test('publishes a generator-ready OpenAPI contract', async () => { const spec = await (await createApp().request('/openapi.json')).json() const operation = spec.paths['/v1/orgs/{orgId}/faucet'].post expect({ amountExamples: spec.components.schemas.FaucetFundedToken.properties.amount.examples, components: [ 'FaucetFundedToken', 'FaucetToken', 'FundTestnetAccountRequest', 'FundTestnetAccountResponse', ].filter((name) => spec.components.schemas[name]), errors: { 400: operation.responses[400].content['application/json'].schema, 404: operation.responses[404].content['application/json'].schema, 429: operation.responses[429].content['application/json'].schema, }, operationId: operation.operationId, request: operation.requestBody.content['application/json'].schema, response: operation.responses[200].content['application/json'].schema, }).toEqual({ amountExamples: [{ baseUnits: '1000000000', currency: 'USD', decimals: 6, formatted: '1000' }], components: [ 'FaucetFundedToken', 'FaucetToken', 'FundTestnetAccountRequest', 'FundTestnetAccountResponse', ], errors: { 400: { $ref: '#/components/schemas/ApiKeyMalformedOrBodyInvalidOrParamInvalidError' }, 404: { $ref: '#/components/schemas/OrganizationNotFoundError' }, 429: { $ref: '#/components/schemas/RateLimitExceededError' }, }, operationId: 'fundTestnetAccount', request: { $ref: '#/components/schemas/FundTestnetAccountRequest' }, response: { $ref: '#/components/schemas/FundTestnetAccountResponse' }, }) }) describe('faucet', () => { test('funds a member account and rate limits repeated funding', async () => { const app = createApp() const cookie = await session(app) const org = await organization(app, cookie) const recipient = privateKeyToAccount(generatePrivateKey()).address const funded = await app.request(`/v1/orgs/${org.id}/faucet`, { body: JSON.stringify({ address: recipient }), headers: { 'content-type': 'application/json', cookie }, method: 'POST', }) expect(funded.status).toBe(200) const result = await TestApp.json(funded, Faucet.schema.FundResponse) expect(result.tokens.length).toBeGreaterThan(0) expect( result.tokens.every( (entry) => entry.token.symbol.length > 0 && BigInt(entry.amount.baseUnits) > 0n && entry.amount.currency === entry.token.currency && entry.amount.decimals === entry.token.decimals && /^\d+(\.\d+)?$/.test(entry.amount.formatted), ), ).toBe(true) expect(result.transactionHashes).toEqual(result.tokens.map((entry) => entry.transactionHash)) const repeated = await app.request(`/v1/orgs/${org.id}/faucet`, { body: JSON.stringify({ address: recipient }), headers: { 'content-type': 'application/json', cookie }, method: 'POST', }) expect(repeated.status).toBe(429) expect(repeated.headers.get('ratelimit-scope')).toBe('faucet-address') }) test('requires membership and a valid address', async () => { const app = createApp() const owner = await session(app) const org = await organization(app, owner) const stranger = await session(app) const inaccessible = await app.request(`/v1/orgs/${org.id}/faucet`, { body: JSON.stringify({ address: privateKeyToAccount(generatePrivateKey()).address }), headers: { 'content-type': 'application/json', cookie: stranger }, method: 'POST', }) expect(inaccessible.status).toBe(404) const invalid = await app.request(`/v1/orgs/${org.id}/faucet`, { body: JSON.stringify({ address: 'not-an-address' }), headers: { 'content-type': 'application/json', cookie: owner }, method: 'POST', }) expect(invalid.status).toBe(400) }) })