Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
130 lines
5.7 KiB
TypeScript
130 lines
5.7 KiB
TypeScript
import { randomUUID } from "node:crypto";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { describe, expect, test } from "vitest";
|
|
import { DatabaseConnection } from "../../src/main/database/connection";
|
|
import { ChatService } from "../../src/main/services/chat-service";
|
|
import { OrganizationService } from "../../src/main/services/organization-service";
|
|
import { SearchService } from "../../src/main/services/search-service";
|
|
import { DocumentService } from "../../src/main/services/document-service";
|
|
import { KnowledgeService } from "../../src/main/services/knowledge-service";
|
|
import { TASK_STATUS } from "../../src/shared/domain";
|
|
import { createId } from "../../src/shared/ids";
|
|
|
|
function createConnection(): DatabaseConnection {
|
|
return new DatabaseConnection(join(tmpdir(), `agentcompany-new-search-${randomUUID()}.db`));
|
|
}
|
|
|
|
describe("search service", () => {
|
|
test("returns faceted full-text results", () => {
|
|
const connection = createConnection();
|
|
const organization = new OrganizationService(connection.db);
|
|
const search = new SearchService(connection.db);
|
|
let orgId = "";
|
|
let employeeId = "";
|
|
|
|
connection.transaction(() => {
|
|
orgId = organization.createOrganization({
|
|
organizationName: "Acme Operations",
|
|
employeeName: "Dana Lee",
|
|
mission: "Coordinate agents with auditable approvals.",
|
|
goal: "Ship a verified control plane.",
|
|
});
|
|
employeeId = (connection.db.prepare("select id from employees where org_id = ?").get(orgId) as { id: string }).id;
|
|
organization.createTask({
|
|
orgId,
|
|
title: "Production deploy",
|
|
description: "Release the production deploy checklist.",
|
|
status: TASK_STATUS.BACKLOG,
|
|
assigneeAgentId: null,
|
|
risk: "high",
|
|
projectId: null,
|
|
goalId: null,
|
|
workspaceId: null,
|
|
priority: "normal",
|
|
requiresFilesystem: false,
|
|
});
|
|
});
|
|
|
|
const results = search.search({ orgId, employeeId, query: "production", entityTypes: ["task"] });
|
|
expect(results).toHaveLength(1);
|
|
expect(results[0]).toMatchObject({ entityType: "task", title: "Production deploy", facets: { type: "Work", risk: "high" } });
|
|
connection.close();
|
|
});
|
|
|
|
test("filters restricted chat messages by membership", () => {
|
|
const connection = createConnection();
|
|
const organization = new OrganizationService(connection.db);
|
|
const chat = new ChatService(connection.db);
|
|
const search = new SearchService(connection.db);
|
|
let orgId = "";
|
|
let ownerId = "";
|
|
let outsiderId = "";
|
|
let spaceId = "";
|
|
|
|
connection.transaction(() => {
|
|
orgId = organization.createOrganization({
|
|
organizationName: "Acme Operations",
|
|
employeeName: "Dana Lee",
|
|
mission: "Coordinate agents with auditable approvals.",
|
|
goal: "Ship a verified control plane.",
|
|
});
|
|
ownerId = (connection.db.prepare("select id from employees where org_id = ?").get(orgId) as { id: string }).id;
|
|
outsiderId = createId("emp");
|
|
connection.db.prepare("insert into employees(id, org_id, display_name, role, created_at) values (?, ?, ?, ?, ?)")
|
|
.run(outsiderId, orgId, "Riley Chen", "Observer", new Date().toISOString());
|
|
spaceId = chat.createSpace({
|
|
orgId,
|
|
spaceType: "project",
|
|
name: "restricted-release",
|
|
topic: "Need-to-know release",
|
|
visibility: "restricted",
|
|
memberEmployeeIds: [ownerId],
|
|
});
|
|
chat.sendMessage({
|
|
orgId,
|
|
spaceId,
|
|
threadId: null,
|
|
senderType: "employee",
|
|
senderId: ownerId,
|
|
bodyMd: "Secret launch window",
|
|
attachmentFileIds: [],
|
|
});
|
|
});
|
|
|
|
expect(search.search({ orgId, employeeId: ownerId, query: "secret", entityTypes: ["chat_message"] })).toHaveLength(1);
|
|
expect(search.search({ orgId, employeeId: outsiderId, query: "secret", entityTypes: ["chat_message"] })).toHaveLength(0);
|
|
connection.close();
|
|
});
|
|
|
|
test("finds documents and knowledge by their bodies, with state facets", () => {
|
|
const connection = createConnection();
|
|
const organization = new OrganizationService(connection.db);
|
|
const search = new SearchService(connection.db);
|
|
let orgId = "";
|
|
connection.transaction(() => {
|
|
orgId = organization.createOrganization({ organizationName: "Lib", employeeName: "Dana Lee", mission: "m", goal: "g" });
|
|
});
|
|
const employeeId = (connection.db.prepare("select id from employees where org_id = ?").get(orgId) as { id: string }).id;
|
|
new DocumentService(connection.db).create({
|
|
orgId, title: "Pricing memo", docType: "memo", content: "Enterprise tier costs twelve hundred dollars.",
|
|
authorAgentId: null, reviewerAgentId: null, projectId: null, goalId: null, taskId: null,
|
|
});
|
|
const knowledge = new KnowledgeService(connection.db);
|
|
const oldId = knowledge.create({
|
|
orgId, topic: "Refund window", category: "policy", content: "Refunds within fourteen days.", importance: "normal",
|
|
sourceType: "employee", sourceId: null, scopeType: "organization", scopeId: null, validUntil: null, supersedesId: null,
|
|
});
|
|
knowledge.create({
|
|
orgId, topic: "Refund window", category: "policy", content: "Refunds within thirty days.", importance: "normal",
|
|
sourceType: "employee", sourceId: null, scopeType: "organization", scopeId: null, validUntil: null, supersedesId: oldId,
|
|
});
|
|
|
|
const docs = search.search({ orgId, employeeId, query: "twelve hundred" });
|
|
expect(docs.map((r) => [r.entityType, r.title, r.facets.kind])).toEqual([["document", "Pricing memo", "memo"]]);
|
|
const entries = search.search({ orgId, employeeId, query: "refunds", entityTypes: ["knowledge"] });
|
|
expect(entries.map((r) => r.facets.state).sort()).toEqual(["active", "superseded"]);
|
|
connection.close();
|
|
});
|
|
});
|