test/home-mate-models.test.js
Rama main · réplica de lectura del repositorio original.
var assert = require("node:assert/strict");
var fs = require("node:fs");
var path = require("node:path");
var test = require("node:test");
var attachHomeChat = require("../lib/server-home-chat").attachHomeChat;
function settle() {
return new Promise(function (resolve) { setImmediate(resolve); });
}
function fixture(options) {
var opts = options || {};
var mateModel = Object.prototype.hasOwnProperty.call(opts, "mateModel") ? opts.mateModel : "fable";
var mateVendor = opts.mateVendor || "claude";
var mate = { id: "mate-a", name: "A", vendor: mateVendor, model: mateModel };
if (opts.builtinClay) mate.builtinKey = "clay";
var sessions = new Map();
if (opts.existingSession) sessions.set(1, opts.existingSession);
var created = [];
var saved = [];
var dispatched = [];
var manager = {
sessions: sessions,
createSession: function (sessionOptions) {
created.push(sessionOptions);
var session = Object.assign({ localId: created.length + 10, history: [], lastActivity: Date.now() }, sessionOptions);
sessions.set(session.localId, session);
return session;
},
subscribeSession: function () { return function () {}; },
sendAndRecord: function (session, event) { session.history.push(event); },
saveSessionFile: opts.noPersistence ? null : function (session) {
if (opts.persistenceError) throw new Error("save failed");
saved.push(session.localId);
},
};
var messages = [];
var ws = {
readyState: 1,
_clayUser: opts.unauthenticated ? null : { id: "u1" },
send: function (value) { messages.push(JSON.parse(value)); },
};
var updates = [];
var project = {
getSessionManager: function () { return manager; },
getVendorModelCatalog: function (catalogWs, vendor) {
var catalog = opts.catalogs && opts.catalogs[vendor] ? opts.catalogs[vendor] : opts.catalog;
return Promise.resolve(catalog || {
status: "ready",
error: "",
models: [
{ value: "fable", resolvedModel: "claude-fable-5", displayName: "Claude Fable" },
{ value: "sonnet", displayName: "Claude Sonnet" },
],
});
},
getVendorModelAvailability: function (availabilityWs) {
if (opts.onAvailability) opts.onAvailability(availabilityWs);
return opts.vendors || [{ id: "claude", displayName: "Claude", installed: true }];
},
sdk: opts.sdk === null ? null : (opts.sdk || {
startQuery: function (session) { dispatched.push(session.model); },
pushMessage: function (session) { dispatched.push(session.model); },
}),
getMemoryState: function () { return { entries: [], summary: "" }; },
listKnowledgeFiles: function () { return []; },
forEachClient: function (fn) { fn(ws); },
};
var handler = attachHomeChat({
users: { isMultiUser: function () { return true; }, findUserById: function (id) { return id === "u1" ? { id: id } : null; } },
mates: {
buildMateCtx: function (userId) { return { userId: userId }; },
getAllMates: function () { return [mate]; },
getMate: function () { return mate; },
getMateDir: function () { return "/tmp/mate-a"; },
updateMate: function (ctx, mateId, patch) {
updates.push({ ctx: ctx, mateId: mateId, patch: patch });
mate = Object.assign({}, mate, patch);
return mate;
},
},
projects: new Map([["mate-mate-a", project]]),
addProject: function () {},
resolveDefaultAi: opts.resolveDefaultAi,
});
return { handler: handler, ws: ws, messages: messages, updates: updates, created: created, sessions: sessions, manager: manager, saved: saved, dispatched: dispatched, getMate: function () { return mate; } };
}
test("cold Home model discovery passes the authenticated socket to availability", async function () {
var received = null;
var f = fixture({ onAvailability: function (availabilityWs) { received = availabilityWs; } });
f.handler.handleMessage(f.ws, { type: "home_mate_models_get", mateId: "mate-a", requestId: "cold-home" });
await settle();
assert.equal(received, f.ws);
assert.equal(received._clayUser.id, "u1");
});
test("Mate model catalog responses preserve request correlation and vendor state", async function () {
var f = fixture();
assert.equal(f.handler.handleMessage(f.ws, { type: "home_mate_models_get", mateId: "mate-a", requestId: "catalog-1" }), true);
await settle();
assert.deepEqual(f.messages[0], {
type: "home_mate_models_state",
mateId: "mate-a",
requestId: "catalog-1",
vendor: "claude",
mateVendor: "claude",
mateModel: "fable",
model: "fable",
models: [
{ value: "fable", resolvedModel: "claude-fable-5", displayName: "Claude Fable" },
{ value: "sonnet", displayName: "Claude Sonnet" },
],
vendors: [{ id: "claude", displayName: "Claude", installed: true }],
status: "ready",
error: "",
});
assert.equal(f.messages.some(function (message) { return message.type === "model_info"; }), false);
});
test("Mate model access errors remain correlated instead of leaving the UI loading", function () {
var f = fixture({ unauthenticated: true });
f.handler.handleMessage(f.ws, { type: "home_mate_models_get", mateId: "mate-a", requestId: "catalog-auth" });
assert.deepEqual(f.messages[0], { type: "home_mate_models_state", mateId: "mate-a", requestId: "catalog-auth", vendor: "", mateVendor: "", mateModel: "", model: "", models: [], vendors: [], status: "error", error: "Not authenticated." });
});
test("Mate model selection validates the Mate vendor catalog before persistence", async function () {
var f = fixture();
f.handler.handleMessage(f.ws, { type: "home_mate_model_set", mateId: "mate-a", vendor: "claude", model: "unknown", requestId: "set-invalid" });
await settle();
assert.equal(f.updates.length, 0);
assert.equal(f.messages[0].type, "home_mate_model_result");
assert.equal(f.messages[0].requestId, "set-invalid");
assert.equal(f.messages[0].ok, false);
assert.match(f.messages[0].error, /not available/);
});
test("valid Mate model selection persists and broadcasts the server-confirmed record", async function () {
var f = fixture();
f.handler.handleMessage(f.ws, { type: "home_mate_model_set", mateId: "mate-a", vendor: "claude", model: "claude-fable-5", requestId: "set-valid" });
await settle();
assert.deepEqual(f.updates[0], { ctx: { userId: "u1" }, mateId: "mate-a", patch: { vendor: "claude", model: "fable" } });
assert.equal(f.getMate().model, "fable");
assert.equal(f.messages[0].type, "mate_updated");
assert.equal(f.messages[0].mate.model, "fable");
assert.deepEqual(f.messages[1], { type: "home_mate_model_result", mateId: "mate-a", requestId: "set-valid", ok: true, vendor: "claude", model: "fable" });
});
test("Mate model chooser loads another configured vendor without partially persisting it", async function () {
var f = fixture({
vendors: [
{ id: "claude", displayName: "Claude", installed: true },
{ id: "codex", displayName: "Codex", installed: true },
],
catalogs: {
claude: { status: "error", models: [], error: "Claude authentication expired." },
codex: { status: "ready", models: [{ value: "gpt-5.6", displayName: "GPT-5.6" }], error: "" },
},
});
f.handler.handleMessage(f.ws, { type: "home_mate_models_get", mateId: "mate-a", vendor: "codex", requestId: "catalog-codex" });
await settle();
assert.equal(f.messages[0].vendor, "codex");
assert.equal(f.messages[0].mateVendor, "claude");
assert.equal(f.messages[0].model, "");
assert.deepEqual(f.messages[0].models, [{ value: "gpt-5.6", displayName: "GPT-5.6" }]);
assert.equal(f.updates.length, 0);
assert.equal(f.getMate().vendor, "claude");
});
test("vendor and concrete model persist atomically and seed new Home sessions", async function () {
var f = fixture({
vendors: [
{ id: "claude", displayName: "Claude", installed: true },
{ id: "codex", displayName: "Codex", installed: true },
],
catalogs: {
codex: { status: "ready", models: [{ value: "gpt-5.6", resolvedModel: "gpt-5.6-codex", displayName: "GPT-5.6" }], error: "" },
},
});
f.handler.handleMessage(f.ws, { type: "home_mate_model_set", mateId: "mate-a", vendor: "codex", model: "gpt-5.6-codex", requestId: "set-codex" });
await settle();
assert.deepEqual(f.updates[0].patch, { vendor: "codex", model: "gpt-5.6" });
assert.equal(f.messages[0].type, "mate_updated");
assert.equal(f.messages[0].mate.vendor, "codex");
assert.deepEqual(f.messages[1], { type: "home_mate_model_result", mateId: "mate-a", requestId: "set-codex", ok: true, vendor: "codex", model: "gpt-5.6" });
f.handler.handleMessage(f.ws, { type: "home_mate_new_session", mateId: "mate-a", requestId: "new-codex" });
await settle();
assert.equal(f.created[0].vendor, "codex");
assert.equal(f.created[0].model, "gpt-5.6");
});
test("fresh builtin Clay sessions use shared Default AI while custom Mates retain their configured runtime", async function () {
var resolver = function () { return Promise.resolve({ ready: true, vendor: "codex", model: "gpt-6-astra", effort: "high" }); };
var clay = fixture({ builtinClay: true, resolveDefaultAi: resolver });
clay.handler.handleMessage(clay.ws, { type: "home_mate_new_session", mateId: "mate-a", requestId: "clay-default" });
await settle();
assert.deepEqual([clay.created[0].vendor, clay.created[0].model, clay.created[0].effort], ["codex", "gpt-6-astra", "high"]);
assert.equal(clay.updates.length, 0, "the shared default does not overwrite the builtin Mate record");
var custom = fixture({ resolveDefaultAi: resolver });
custom.handler.handleMessage(custom.ws, { type: "home_mate_new_session", mateId: "mate-a", requestId: "custom-config" });
await settle();
assert.deepEqual([custom.created[0].vendor, custom.created[0].model], ["claude", "fable"]);
});
test("composer model selection updates the same pristine Home draft and its next send", async function () {
var f = fixture({
vendors: [
{ id: "claude", displayName: "Claude", installed: true },
{ id: "codex", displayName: "Codex", installed: true },
],
catalogs: { codex: { status: "ready", models: [{ value: "gpt-5.6", resolvedModel: "gpt-5.6-codex" }] } },
});
f.handler.handleMessage(f.ws, { type: "home_mate_new_session", mateId: "mate-a", requestId: "new-draft" });
await settle();
var history = f.messages.filter(function (message) { return message.type === "home_mate_history"; })[0];
assert.equal(history.sessionId, "local:11");
f.handler.handleMessage(f.ws, { type: "home_mate_model_set", mateId: "mate-a", vendor: "codex", model: "gpt-5.6-codex", sessionId: history.sessionId, requestId: "draft-model" });
await settle();
var result = f.messages.filter(function (message) { return message.type === "home_mate_model_result"; })[0];
assert.equal(result.ok, true);
assert.equal(result.requestedSessionId, "local:11");
assert.equal(result.sessionId, "local:11");
assert.equal(result.sessionApplied, true);
assert.equal(result.sessionVendor, "codex");
assert.equal(result.sessionModel, "gpt-5.6");
assert.equal(f.sessions.get(11).vendor, "codex");
assert.equal(f.sessions.get(11).model, "gpt-5.6");
assert.deepEqual(f.saved, [11]);
assert.equal(f.created.length, 1);
assert.deepEqual(f.getMate(), { id: "mate-a", name: "A", vendor: "codex", model: "gpt-5.6" });
f.handler.handleMessage(f.ws, { type: "home_mate_send", mateId: "mate-a", sessionId: "local:11", requestId: "new-draft", text: "Use the draft model" });
await settle();
assert.deepEqual(f.dispatched, ["gpt-5.6"]);
assert.equal(f.created.length, 1);
});
test("activity, processing, stale, and other-user sessions remain immutable while the Mate default updates", async function () {
var activityTypes = ["user_message", "delta", "tool_start", "tool_result"];
for (var i = 0; i < activityTypes.length; i++) {
var session = { localId: 1, cliSessionId: "activity-" + i, ownerId: "u1", vendor: "claude", model: "fable", history: [{ type: activityTypes[i] }], lastActivity: 20 };
var active = fixture({ existingSession: session });
active.handler.handleMessage(active.ws, { type: "home_mate_model_set", mateId: "mate-a", vendor: "claude", model: "sonnet", sessionId: session.cliSessionId, requestId: "activity-set-" + i });
await settle();
var activeResult = active.messages.filter(function (message) { return message.type === "home_mate_model_result"; })[0];
assert.equal(activeResult.sessionApplied, false);
assert.match(activeResult.sessionReason, /already has activity/);
assert.equal(session.model, "fable");
assert.deepEqual(active.saved, []);
assert.equal(active.getMate().model, "sonnet");
}
var processingSession = { localId: 1, cliSessionId: "processing", ownerId: "u1", vendor: "claude", model: "fable", history: [], isProcessing: true };
var processing = fixture({ existingSession: processingSession });
processing.handler.handleMessage(processing.ws, { type: "home_mate_model_set", mateId: "mate-a", vendor: "claude", model: "sonnet", sessionId: "processing", requestId: "processing-set" });
await settle();
assert.equal(processingSession.model, "fable");
assert.equal(processing.messages.filter(function (message) { return message.type === "home_mate_model_result"; })[0].sessionApplied, false);
var owned = { localId: 1, cliSessionId: "owned", ownerId: "u1", vendor: "claude", model: "fable", history: [] };
var inaccessible = fixture({ existingSession: owned });
inaccessible.sessions.set(2, { localId: 2, cliSessionId: "other-user", ownerId: "u2", vendor: "claude", model: "fable", history: [] });
inaccessible.handler.handleMessage(inaccessible.ws, { type: "home_mate_model_set", mateId: "mate-a", vendor: "claude", model: "sonnet", sessionId: "other-user", requestId: "other-set" });
await settle();
var inaccessibleResult = inaccessible.messages.filter(function (message) { return message.type === "home_mate_model_result"; })[0];
assert.equal(inaccessibleResult.sessionApplied, false);
assert.equal(inaccessible.sessions.get(2).model, "fable");
assert.deepEqual(inaccessible.saved, []);
});
test("a draft persistence failure rolls back in-memory session metadata", async function () {
var session = { localId: 1, cliSessionId: "draft-save-error", ownerId: "u1", vendor: "claude", model: "fable", history: [] };
var f = fixture({ existingSession: session, persistenceError: true });
f.handler.handleMessage(f.ws, { type: "home_mate_model_set", mateId: "mate-a", vendor: "claude", model: "sonnet", sessionId: "draft-save-error", requestId: "draft-save" });
await settle();
var result = f.messages.filter(function (message) { return message.type === "home_mate_model_result"; })[0];
assert.equal(result.ok, true);
assert.equal(result.sessionApplied, false);
assert.match(result.sessionReason, /could not be persisted/);
assert.equal(session.vendor, "claude");
assert.equal(session.model, "fable");
});
test("vendor-only, unavailable-vendor, and cross-catalog selections never persist", async function () {
var options = {
vendors: [
{ id: "claude", displayName: "Claude", installed: true },
{ id: "codex", displayName: "Codex", installed: true },
],
catalogs: { codex: { status: "ready", models: ["gpt-5.6"], error: "" } },
};
var missing = fixture(options);
missing.handler.handleMessage(missing.ws, { type: "home_mate_model_set", mateId: "mate-a", vendor: "codex", model: "", requestId: "missing-model" });
await settle();
assert.equal(missing.updates.length, 0);
assert.equal(missing.messages[0].ok, false);
var unavailable = fixture(options);
unavailable.handler.handleMessage(unavailable.ws, { type: "home_mate_model_set", mateId: "mate-a", vendor: "gemini", model: "gemini-2", requestId: "bad-vendor" });
await settle();
assert.equal(unavailable.updates.length, 0);
assert.match(unavailable.messages[0].error, /not configured/);
var mismatched = fixture(options);
mismatched.handler.handleMessage(mismatched.ws, { type: "home_mate_model_set", mateId: "mate-a", vendor: "codex", model: "sonnet", requestId: "cross-catalog" });
await settle();
assert.equal(mismatched.updates.length, 0);
assert.match(mismatched.messages[0].error, /not available/);
});
test("Mate model resolver repairs invalid records with the saved catalog default then first entry", async function () {
var preferred = fixture({
mateModel: "removed-model",
catalog: { status: "ready", models: ["fable", "sonnet"], defaultModel: "sonnet", error: "" },
});
preferred.handler.handleMessage(preferred.ws, { type: "home_mate_new_session", mateId: "mate-a", requestId: "new-preferred" });
await settle();
assert.equal(preferred.getMate().model, "sonnet");
assert.equal(preferred.created[0].model, "sonnet");
assert.equal(preferred.messages[0].type, "mate_updated");
assert.equal(preferred.messages[0].mate.model, "sonnet");
var first = fixture({
mateModel: "removed-model",
catalog: { status: "ready", models: [{ displayName: "Unavailable heading" }, "fable", "sonnet"], defaultModel: "removed-default", error: "" },
});
first.handler.handleMessage(first.ws, { type: "home_mate_new_session", mateId: "mate-a", requestId: "new-first" });
await settle();
assert.equal(first.getMate().model, "fable");
assert.equal(first.created[0].model, "fable");
});
test("catalog failures create no Home session and return an actionable correlated error", async function () {
var f = fixture({ mateModel: null, catalog: { status: "error", models: [], error: "Vendor authentication expired. Reconnect the vendor and try again." } });
f.handler.handleMessage(f.ws, { type: "home_mate_new_session", mateId: "mate-a", requestId: "new-error" });
await settle();
assert.equal(f.created.length, 0);
assert.equal(f.messages[0].type, "home_mate_error");
assert.equal(f.messages[0].requestId, "new-error");
assert.equal(f.messages[0].code, "model_unavailable");
assert.match(f.messages[0].text, /authentication expired/);
});
test("automatic Home open resolves a concrete model and enables the correlated send path", async function () {
var f = fixture({ mateModel: null, catalog: { status: "ready", models: ["sonnet"], defaultModel: "sonnet", error: "" } });
f.handler.handleMessage(f.ws, { type: "home_mate_open", mateId: "mate-a", requestId: "open-resolve" });
await settle();
var history = f.messages.filter(function (message) { return message.type === "home_mate_history"; })[0];
assert.equal(history.requestId, "open-resolve");
assert.equal(history.vendor, "claude");
assert.equal(history.model, "sonnet");
assert.equal(f.getMate().model, "sonnet");
f.handler.handleMessage(f.ws, { type: "home_mate_send", mateId: "mate-a", sessionId: history.sessionId, requestId: "open-resolve", text: "Hello" });
await settle();
assert.deepEqual(f.dispatched, ["sonnet"]);
});
test("catalog outages do not block exact or default resume of concrete sessions", async function () {
var exactSession = { localId: 1, cliSessionId: "exact-ready", ownerId: "u1", vendor: "codex", model: "gpt-5.6", history: [], lastActivity: 20 };
var exact = fixture({ mateModel: null, existingSession: exactSession, catalog: { status: "error", models: [], error: "Vendor authentication expired." } });
exact.handler.handleMessage(exact.ws, { type: "home_mate_session_open", mateId: "mate-a", sessionId: "exact-ready", requestId: "exact-offline" });
await settle();
assert.equal(exact.messages[0].type, "home_mate_history");
assert.equal(exact.messages[0].model, "gpt-5.6");
assert.equal(exact.messages[0].requestId, "exact-offline");
assert.deepEqual(exact.saved, []);
assert.deepEqual(exact.updates, []);
var latestSession = { localId: 1, cliSessionId: "latest-ready", ownerId: "u1", vendor: "claude", model: "sonnet", history: [], lastActivity: 30 };
var latest = fixture({ mateModel: null, existingSession: latestSession, catalog: { status: "error", models: [], error: "Vendor authentication expired." } });
latest.handler.handleMessage(latest.ws, { type: "home_mate_open", mateId: "mate-a", requestId: "default-offline" });
await settle();
assert.equal(latest.messages[0].type, "home_mate_history");
assert.equal(latest.messages[0].sessionId, "latest-ready");
assert.equal(latest.messages[0].model, "sonnet");
assert.deepEqual(latest.saved, []);
assert.deepEqual(latest.updates, []);
});
test("legacy model-less sessions commit once while concrete sessions remain immutable", async function () {
var legacySession = { localId: 1, cliSessionId: "legacy", ownerId: "u1", vendor: "claude", model: null, history: [], lastActivity: 20 };
var legacy = fixture({ mateModel: "sonnet", existingSession: legacySession });
legacy.handler.handleMessage(legacy.ws, { type: "home_mate_session_open", mateId: "mate-a", sessionId: "legacy", requestId: "legacy-open" });
await settle();
assert.equal(legacySession.model, "sonnet");
assert.deepEqual(legacy.saved, [1]);
legacy.handler.handleMessage(legacy.ws, { type: "home_mate_session_open", mateId: "mate-a", sessionId: "legacy", requestId: "legacy-reopen" });
await settle();
assert.deepEqual(legacy.saved, [1]);
var concreteSession = { localId: 1, cliSessionId: "concrete", ownerId: "u1", vendor: "claude", model: "fable", history: [], lastActivity: 20 };
var concrete = fixture({ mateModel: "sonnet", existingSession: concreteSession });
concrete.handler.handleMessage(concrete.ws, { type: "home_mate_session_open", mateId: "mate-a", sessionId: "concrete", requestId: "concrete-open" });
await settle();
assert.equal(concreteSession.model, "fable");
assert.deepEqual(concrete.saved, []);
});
test("legacy session mutation waits for metadata persistence capability", async function () {
var session = { localId: 1, cliSessionId: "legacy-no-save", ownerId: "u1", vendor: "legacy-vendor", model: null, history: [], lastActivity: 20 };
var f = fixture({ mateModel: "sonnet", existingSession: session, noPersistence: true });
f.handler.handleMessage(f.ws, { type: "home_mate_session_open", mateId: "mate-a", sessionId: "legacy-no-save", requestId: "legacy-no-save-open" });
await settle();
assert.equal(session.vendor, "legacy-vendor");
assert.equal(session.model, null);
assert.deepEqual(f.updates, []);
assert.equal(f.messages[0].type, "home_mate_error");
assert.equal(f.messages[0].requestId, "legacy-no-save-open");
assert.equal(f.messages[0].sessionId, "legacy-no-save");
assert.equal(f.messages[0].code, "model_unavailable");
assert.match(f.messages[0].text, /persist/);
});
test("Home send resolves a legacy model before dispatch and blocks when no catalog is available", async function () {
var session = { localId: 1, cliSessionId: "legacy-send", ownerId: "u1", vendor: "claude", model: "", history: [], lastActivity: 20 };
var ready = fixture({ mateModel: "sonnet", existingSession: session });
ready.handler.handleMessage(ready.ws, { type: "home_mate_session_open", mateId: "mate-a", sessionId: "legacy-send", requestId: "send-open" });
await settle();
ready.handler.handleMessage(ready.ws, { type: "home_mate_send", mateId: "mate-a", sessionId: "legacy-send", requestId: "send-open", text: "Hello" });
await settle();
assert.deepEqual(ready.dispatched, ["sonnet"]);
var blockedSession = { localId: 1, cliSessionId: "blocked", ownerId: "u1", vendor: "claude", model: null, history: [], lastActivity: 20 };
var blocked = fixture({ mateModel: null, existingSession: blockedSession, catalog: { status: "empty", models: [], error: "No installed models." } });
blocked.ws._homeChatTap = { mateId: "mate-a", sessionId: 1, sessionReference: "blocked", requestId: "blocked-send" };
blocked.handler.handleMessage(blocked.ws, { type: "home_mate_send", mateId: "mate-a", sessionId: "blocked", requestId: "blocked-send", text: "Hello" });
await settle();
assert.deepEqual(blocked.dispatched, []);
assert.equal(blocked.messages[0].code, "model_unavailable");
assert.equal(blocked.messages[0].requestId, "blocked-send");
});
test("new Home sessions inherit the Mate model while existing sessions remain unchanged", async function () {
var fresh = fixture({ mateModel: "sonnet" });
fresh.handler.handleMessage(fresh.ws, { type: "home_mate_open", mateId: "mate-a" });
await settle();
fresh.handler.handleMessage(fresh.ws, { type: "home_mate_new_session", mateId: "mate-a" });
await settle();
assert.equal(fresh.created.length, 2);
assert.deepEqual(fresh.created.map(function (options) { return [options.vendor, options.model]; }), [["claude", "sonnet"], ["claude", "sonnet"]]);
var existingSession = { localId: 1, cliSessionId: "existing", ownerId: "u1", vendor: "claude", model: "fable", history: [], lastActivity: 20 };
var existing = fixture({ mateModel: "sonnet", existingSession: existingSession });
existing.handler.handleMessage(existing.ws, { type: "home_mate_open", mateId: "mate-a" });
await settle();
assert.equal(existing.created.length, 0);
assert.equal(existingSession.model, "fable");
});
test("Mate model messages route through Home ownership and cannot bypass catalog validation", function () {
var root = path.join(__dirname, "..");
var projectSource = fs.readFileSync(path.join(root, "lib/project.js"), "utf8");
var schemaSource = fs.readFileSync(path.join(root, "lib/ws-schema.js"), "utf8");
var matesSource = fs.readFileSync(path.join(root, "lib/server-mates.js"), "utf8");
var homeSource = fs.readFileSync(path.join(root, "lib/server-home-chat.js"), "utf8");
var homeModelsSource = fs.readFileSync(path.join(root, "lib/server-home-models.js"), "utf8");
assert.match(projectSource, /home_mate_models_get[\s\S]*home_mate_model_set[\s\S]*opts\.onDmMessage/);
assert.match(schemaSource, /"home_mate_models_get"[\s\S]*"home_mate_model_set"[\s\S]*"home_mate_models_state"[\s\S]*"home_mate_model_result"/);
assert.match(homeSource, /findMateProject\(userId, msg\.mateId, true\)/);
assert.match(homeModelsSource, /catalogModel\(catalog\.models \|\| \[\], msg\.model\)/);
assert.match(matesSource, /hasOwnProperty\.call\(msg\.updates, "model"\)[\s\S]*Use the Mate model selector/);
});