ai-prompt-config.repository.test.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import assert from 'node:assert/strict';
  2. import test from 'node:test';
  3. import type { ParseObject } from '../src/db/parse-rest.client.js';
  4. import { VOC_PARSE_CLASSES } from '../src/db/parse-rest.schema.js';
  5. import {
  6. ParseRestAiPromptConfigStore,
  7. type AiPromptConfigRecord,
  8. } from '../src/modules/ai-gateway/prompt-config.repository.js';
  9. type StoredPrompt = AiPromptConfigRecord & ParseObject;
  10. class FakePromptClient {
  11. readonly records: StoredPrompt[] = [];
  12. private nextId = 1;
  13. async findAll<T>(className: string, where: Record<string, unknown> = {}): Promise<Array<T & ParseObject>> {
  14. assert.equal(className, VOC_PARSE_CLASSES.promptConfig);
  15. return this.records.filter((record) => matches(record, where)) as unknown as Array<T & ParseObject>;
  16. }
  17. async findOne<T>(className: string, where: Record<string, unknown>): Promise<(T & ParseObject) | null> {
  18. assert.equal(className, VOC_PARSE_CLASSES.promptConfig);
  19. return (this.records.find((record) => matches(record, where)) ?? null) as (T & ParseObject) | null;
  20. }
  21. async create<T extends Record<string, unknown>>(className: string, object: T): Promise<ParseObject> {
  22. assert.equal(className, VOC_PARSE_CLASSES.promptConfig);
  23. const now = '2026-07-27T00:00:00.000Z';
  24. const stored = {
  25. ...object,
  26. objectId: `prompt-${this.nextId++}`,
  27. createdAt: now,
  28. updatedAt: now,
  29. } as unknown as StoredPrompt;
  30. this.records.push(stored);
  31. return stored;
  32. }
  33. async update<T extends Record<string, unknown>>(
  34. className: string,
  35. objectId: string,
  36. patch: T,
  37. ): Promise<{ updatedAt: string }> {
  38. assert.equal(className, VOC_PARSE_CLASSES.promptConfig);
  39. const record = this.records.find((item) => item.objectId === objectId);
  40. assert.ok(record);
  41. const updatedAt = '2026-07-27T01:00:00.000Z';
  42. Object.assign(record, patch, { updatedAt });
  43. return { updatedAt };
  44. }
  45. }
  46. function matches(record: StoredPrompt, where: Record<string, unknown>): boolean {
  47. return Object.entries(where).every(([key, value]) => record[key as keyof StoredPrompt] === value);
  48. }
  49. function prompt(promptKey: string, template: string): AiPromptConfigRecord {
  50. return {
  51. promptKey,
  52. name: promptKey,
  53. module: 'test',
  54. scope: 'analysis',
  55. template,
  56. enabled: true,
  57. variables: [],
  58. dataSources: [],
  59. };
  60. }
  61. test('AI prompt store isolates workspaces and seeds defaults idempotently', async () => {
  62. const client = new FakePromptClient();
  63. const otherWorkspace = new ParseRestAiPromptConfigStore(client, 'other');
  64. const demashi = new ParseRestAiPromptConfigStore(client, 'demashi');
  65. await otherWorkspace.upsert('shared.analysisPanel.defaultSystem', prompt('shared.analysisPanel.defaultSystem', 'other'));
  66. const first = await demashi.ensureDefaults([
  67. prompt('shared.analysisPanel.defaultSystem', 'demashi default'),
  68. prompt('global.analysis.defaultModel', 'deepseek-v4-pro'),
  69. ]);
  70. const second = await demashi.ensureDefaults([
  71. prompt('shared.analysisPanel.defaultSystem', 'must not overwrite'),
  72. prompt('global.analysis.defaultModel', 'must not overwrite'),
  73. ]);
  74. assert.deepEqual(first, { created: 2, existing: 0 });
  75. assert.deepEqual(second, { created: 0, existing: 2 });
  76. assert.equal((await demashi.list()).length, 2);
  77. assert.equal((await otherWorkspace.list())[0]?.template, 'other');
  78. assert.equal((await demashi.list())[0]?.workspaceId, 'demashi');
  79. assert.equal((await demashi.list())[0]?.revision, 1);
  80. assert.equal((await demashi.list())[1]?.model, 'deepseek-v4-pro');
  81. const updated = await demashi.upsert(
  82. 'shared.analysisPanel.defaultSystem',
  83. prompt('shared.analysisPanel.defaultSystem', 'customized'),
  84. );
  85. assert.equal(updated.template, 'customized');
  86. assert.equal(updated.revision, 2);
  87. assert.equal(client.records.length, 3);
  88. });