test/linear-launch.test.js
Rama main · réplica de lectura del repositorio original.
var test = require('node:test');
var assert = require('node:assert/strict');
var start = require('../lib/project-linear-launch').attachLinearLaunch;
function world() {
var calls = 0, created = 0, saved = 0, sessions = new Map(), events = [];
var ws = { readyState: 1, _clayUser: { id: 'owner' } };
var ctx = { osUsers: true, getLinuxUserForSession: function () { return 'owner-os'; }, ensureProjectAccessForSession: function () { return 'owner-os'; },
getProjectAccess: function () { return { visibility: 'public' }; }, resolveDefaultAi: async function () { return { ready: true, vendor: 'claude', model: 'model' }; }, onProcessingChanged: function () {},
sm: { sessions: sessions, installedVendors: ['claude'], createSessionRaw: function (options) { var session = Object.assign({ localId: ++created, history: [] }, options); sessions.set(session.localId, session); return session; },
saveSessionFile: function () { saved++; return true; }, deleteSession: function (id) { sessions.delete(id); }, switchSession: function () {}, sendAndRecord: function (session, event) { events.push(event); }, sendToSession: function () {}, broadcastSessionList: function () {} },
getSdk: function () { return { startQuery: async function (session, prompt) { calls++; assert.match(prompt, /Do not commit/); assert.match(prompt, /Requirements/); session.queryInstance = {}; } }; } };
var issue = { id: 'id', identifier: 'ENG-123', url: 'https://linear.app/acme/issue/ENG-123', title: 'Fix', description: 'Requirements', comments: [], pageInfo: { hasNextPage: false } };
return { ctx: ctx, issue: issue, ws: ws, launch: start(ctx), sessions: sessions, calls: function () { return calls; }, created: function () { return created; }, events: events };
}
test('starting Linear work coalesces requests, preserves context and reopens the existing session', async function () {
var w = world();
var results = await Promise.all([w.launch(w.ws, w.issue, function () {}), w.launch(w.ws, w.issue, function () {})]);
assert.equal(w.calls(), 1); assert.equal(w.created(), 1); assert.equal(results[0].sessionId, results[1].sessionId);
var session = w.sessions.get(1); assert.equal(session.linearLinks[0].description, undefined); assert.equal(session.ownerId, 'owner');
assert.equal(session.sessionVisibility, 'private');
var existing = await w.launch(w.ws, w.issue, function () {}); assert.equal(existing.existing, true); assert.equal(w.calls(), 1);
});
test('revoked access after runtime selection prevents session creation', async function () {
var w = world(), allowed = true;
w.ctx.resolveDefaultAi = async function () { allowed = false; return { ready: true, vendor: 'claude' }; };
await assert.rejects(w.launch(w.ws, w.issue, function () { if (!allowed) throw new Error('revoked'); }), /revoked/);
assert.equal(w.created(), 0);
});
test('OS identity changes and persistence failures do not dispatch', async function () {
var w = world(); w.ctx.ensureProjectAccessForSession = function () { return 'other'; };
await assert.rejects(w.launch(w.ws, w.issue, function () {}), /identity/); assert.equal(w.calls(), 0); assert.equal(w.sessions.size, 0);
w = world(); w.ctx.sm.saveSessionFile = function () { return false; };
await assert.rejects(w.launch(w.ws, w.issue, function () {}), /save/); assert.equal(w.calls(), 0); assert.equal(w.sessions.size, 0);
});
test('runtime failure retains the linked conversation with an actionable error', async function () {
var w = world(); w.ctx.getSdk = function () { return { startQuery: async function () { throw new Error('runtime unavailable'); } }; };
await assert.rejects(w.launch(w.ws, w.issue, function () {}), function (error) { return error.sessionId === 1; });
assert.equal(w.sessions.get(1).isProcessing, false); assert.equal(w.events.at(-1).type, 'error');
});