"use strict"; const test = require("node:test"); const assert = require("node:assert/strict"); const { XArtAudioEngine } = require("../src/lib/engine"); function createEngine() { return new XArtAudioEngine({ creditConfig: { baseCredits: 1, includedChars: 25000, stepChars: 10000, stepCredits: 1, maxCharsPerArticle: 120000, }, }); } test("returns not_article and does not charge caller", () => { const engine = createEngine(); engine.topUpCredits("u1", 5, "topup-1"); const result = engine.processMention({ mentionPostId: "m1", callerUserId: "u1", parentPost: { id: "p1", text: "plain post" }, }); assert.equal(result.ok, false); assert.equal(result.status, "not_article"); assert.equal(engine.getWalletBalance("u1"), 5); }); test("charges credits and creates completed job for valid article", () => { const engine = createEngine(); engine.topUpCredits("u1", 5, "topup-2"); const result = engine.processMention({ mentionPostId: "m2", callerUserId: "u1", parentPost: { id: "p2", authorId: "author-1", article: { id: "art-2", title: "Long Form", body: "hello ".repeat(2000), }, }, }); assert.equal(result.ok, true); assert.equal(result.job.status, "completed"); assert.equal(result.job.creditsCharged, 1); assert.equal(engine.getWalletBalance("u1"), 4); const ownerAccess = engine.checkAudioAccess(result.job.assetId, "u1"); assert.equal(ownerAccess.allowed, true); }); test("deduplicates repeated mention post ids", () => { const engine = createEngine(); engine.topUpCredits("u1", 5, "topup-3"); const payload = { mentionPostId: "m3", callerUserId: "u1", parentPost: { id: "p3", authorId: "author-2", article: { id: "art-3", title: "Article", body: "text", }, }, }; const first = engine.processMention(payload); const second = engine.processMention(payload); assert.equal(first.ok, true); assert.equal(second.ok, true); assert.equal(second.deduped, true); assert.equal(engine.getWalletBalance("u1"), 4); }); test("non-owner must pay same credits to unlock, then has permanent access", () => { const engine = createEngine(); engine.topUpCredits("u1", 5, "topup-4-owner"); engine.topUpCredits("u2", 5, "topup-4-viewer"); const result = engine.processMention({ mentionPostId: "m4", callerUserId: "u1", parentPost: { id: "p4", authorId: "author-2", article: { id: "art-4", title: "Article", body: "content", }, }, }); const checkBefore = engine.checkAudioAccess(result.job.assetId, "u2"); assert.equal(checkBefore.allowed, false); assert.equal(checkBefore.reason, "payment_required"); assert.equal(checkBefore.creditsRequired, result.job.creditsCharged); engine.unlockAudio(result.job.assetId, "u2"); assert.equal(engine.getWalletBalance("u2"), 4); const checkAfter = engine.checkAudioAccess(result.job.assetId, "u2"); assert.equal(checkAfter.allowed, true); engine.unlockAudio(result.job.assetId, "u2"); assert.equal(engine.getWalletBalance("u2"), 4); }); test("lists jobs for user newest first and provides summary", () => { const engine = createEngine(); engine.topUpCredits("u1", 10, "topup-5"); engine.processMention({ mentionPostId: "m5a", callerUserId: "u1", parentPost: { id: "p5a", authorId: "author-a", article: { id: "a5a", title: "One", body: "x" }, }, }); engine.processMention({ mentionPostId: "m5b", callerUserId: "u1", parentPost: { id: "p5b", authorId: "author-b", article: { id: "a5b", title: "Two", body: "y" }, }, }); const jobs = engine.listJobsForUser("u1"); assert.equal(jobs.length, 2); assert.equal(jobs[0].mentionPostId, "m5b"); assert.equal(jobs[1].mentionPostId, "m5a"); const summary = engine.getUserSummary("u1"); assert.equal(summary.totalJobs, 2); assert.equal(summary.completedJobs, 2); assert.equal(summary.totalCreditsSpent, 2); assert.equal(summary.balance, 8); }); test("round-trips state snapshot across engine restart", () => { const engine1 = createEngine(); engine1.topUpCredits("u1", 5, "topup-snapshot"); const created = engine1.processMention({ mentionPostId: "m-snapshot", callerUserId: "u1", parentPost: { id: "p-snapshot", authorId: "author-snapshot", article: { id: "a-snapshot", title: "Snap", body: "hello world" }, }, }); const snapshot = engine1.exportState(); const engine2 = new XArtAudioEngine({ creditConfig: engine1.creditConfig, initialState: snapshot, }); assert.equal(engine2.getWalletBalance("u1"), 4); assert.equal(engine2.getJob(created.job.id).article.title, "Snap"); assert.equal(engine2.getAsset(created.job.assetId).articleTitle, "Snap"); assert.equal(engine2.checkAudioAccess(created.job.assetId, "u1").allowed, true); });