chore: exclude local resource artifacts from main sync
This commit is contained in:
@@ -1,20 +1,21 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {
|
||||
mergeDefaultChatTypes,
|
||||
migrateLegacyChatTypeContexts,
|
||||
stripBuiltInChatTypes,
|
||||
sanitizePersistedChatTypes,
|
||||
resolveAppConfigByOrigin,
|
||||
resolveCanonicalChatTypesFromConfig,
|
||||
resolveCanonicalChatContextSettingsFromConfig,
|
||||
stripChatContextSettingsFromScopedAppConfigs,
|
||||
stripSharedContextDataFromScopedAppConfigs,
|
||||
} from './app-config-service.js';
|
||||
|
||||
test('mergeDefaultChatTypes preserves saved edits for built-in chat types', () => {
|
||||
const merged = mergeDefaultChatTypes([
|
||||
test('sanitizePersistedChatTypes keeps saved chat type edits as-is', () => {
|
||||
const merged = sanitizePersistedChatTypes([
|
||||
{
|
||||
id: 'general-request',
|
||||
name: '일반 요청',
|
||||
sortOrder: 3,
|
||||
description: '사용자가 수정한 일반 요청 문맥',
|
||||
permissions: ['guest', 'token-user'],
|
||||
enabled: true,
|
||||
@@ -27,13 +28,15 @@ test('mergeDefaultChatTypes preserves saved edits for built-in chat types', () =
|
||||
assert.ok(generalRequest);
|
||||
assert.equal(generalRequest.description, '사용자가 수정한 일반 요청 문맥');
|
||||
assert.deepEqual(generalRequest.permissions, ['guest', 'token-user']);
|
||||
assert.equal(generalRequest.sortOrder, 1);
|
||||
});
|
||||
|
||||
test('mergeDefaultChatTypes preserves saved edits for layout editor execution', () => {
|
||||
const merged = mergeDefaultChatTypes([
|
||||
test('sanitizePersistedChatTypes keeps saved layout editor execution entries', () => {
|
||||
const merged = sanitizePersistedChatTypes([
|
||||
{
|
||||
id: 'layout-editor-execution',
|
||||
name: 'Layout editor 실행',
|
||||
sortOrder: 2,
|
||||
description: '호출 가능한 API 요청만 처리합니다.',
|
||||
permissions: ['token-user'],
|
||||
enabled: true,
|
||||
@@ -45,13 +48,15 @@ test('mergeDefaultChatTypes preserves saved edits for layout editor execution',
|
||||
|
||||
assert.ok(layoutEditorExecution);
|
||||
assert.equal(layoutEditorExecution.description, '호출 가능한 API 요청만 처리합니다.');
|
||||
assert.equal(layoutEditorExecution.sortOrder, 1);
|
||||
});
|
||||
|
||||
test('mergeDefaultChatTypes preserves saved edits for guided layout editor execution', () => {
|
||||
const merged = mergeDefaultChatTypes([
|
||||
test('sanitizePersistedChatTypes keeps saved guided layout editor entries', () => {
|
||||
const merged = sanitizePersistedChatTypes([
|
||||
{
|
||||
id: 'layout-editor-guided-execution',
|
||||
name: 'Layout editor 단계별 실행',
|
||||
sortOrder: 4,
|
||||
description: '사용자가 정리한 단계별 Layout 실행 문맥',
|
||||
permissions: ['token-user'],
|
||||
enabled: true,
|
||||
@@ -63,24 +68,48 @@ test('mergeDefaultChatTypes preserves saved edits for guided layout editor execu
|
||||
|
||||
assert.ok(guidedLayoutEditorExecution);
|
||||
assert.equal(guidedLayoutEditorExecution.description, '사용자가 정리한 단계별 Layout 실행 문맥');
|
||||
assert.equal(guidedLayoutEditorExecution.sortOrder, 1);
|
||||
});
|
||||
|
||||
test('mergeDefaultChatTypes still appends missing built-in chat types', () => {
|
||||
const merged = mergeDefaultChatTypes([]);
|
||||
test('sanitizePersistedChatTypes returns empty list when nothing is saved', () => {
|
||||
const merged = sanitizePersistedChatTypes([]);
|
||||
|
||||
assert.ok(merged.some((item) => item.id === 'general-request'));
|
||||
assert.ok(merged.some((item) => item.id === 'layout-editor-execution'));
|
||||
assert.ok(merged.some((item) => item.id === 'api-request-template'));
|
||||
assert.ok(merged.some((item) => item.id === 'general-inquiry'));
|
||||
assert.ok(!merged.some((item) => item.id === 'plan-checklist-execution'));
|
||||
assert.ok(!merged.some((item) => item.id === 'layout-editor-guided-execution'));
|
||||
assert.deepEqual(merged, []);
|
||||
});
|
||||
|
||||
test('stripBuiltInChatTypes removes built-in chat type ids from saved list', () => {
|
||||
const stripped = stripBuiltInChatTypes([
|
||||
test('sanitizePersistedChatTypes keeps saved chat type list without backfilling removed entries', () => {
|
||||
const merged = sanitizePersistedChatTypes([
|
||||
{
|
||||
id: 'general-request',
|
||||
name: '일반 요청',
|
||||
sortOrder: 2,
|
||||
description: '사용자가 수정한 일반 요청 문맥',
|
||||
permissions: ['token-user'],
|
||||
enabled: true,
|
||||
updatedAt: '2026-05-08T00:00:00.000Z',
|
||||
},
|
||||
{
|
||||
id: 'custom-support-flow',
|
||||
name: '운영 문의 전용',
|
||||
sortOrder: 1,
|
||||
description: 'custom',
|
||||
permissions: ['token-user'],
|
||||
enabled: true,
|
||||
updatedAt: '2026-05-08T00:00:00.000Z',
|
||||
},
|
||||
]);
|
||||
|
||||
assert.ok(!merged.some((item) => item.id === 'layout-editor-guided-execution'));
|
||||
assert.ok(!merged.some((item) => item.id === 'layout-editor-execution'));
|
||||
assert.ok(merged.some((item) => item.id === 'custom-support-flow'));
|
||||
});
|
||||
|
||||
test('sanitizePersistedChatTypes keeps all saved chat types without special filtering', () => {
|
||||
const stripped = sanitizePersistedChatTypes([
|
||||
{
|
||||
id: 'general-request',
|
||||
name: '일반 요청',
|
||||
sortOrder: 2,
|
||||
description: 'builtin',
|
||||
permissions: ['token-user'],
|
||||
enabled: true,
|
||||
@@ -89,6 +118,7 @@ test('stripBuiltInChatTypes removes built-in chat type ids from saved list', ()
|
||||
{
|
||||
id: 'plan-checklist-execution',
|
||||
name: 'Plan 체크리스트 실행',
|
||||
sortOrder: 3,
|
||||
description: 'custom-seeded',
|
||||
permissions: ['token-user'],
|
||||
enabled: true,
|
||||
@@ -97,6 +127,7 @@ test('stripBuiltInChatTypes removes built-in chat type ids from saved list', ()
|
||||
{
|
||||
id: 'custom-support-flow',
|
||||
name: '운영 문의 전용',
|
||||
sortOrder: 1,
|
||||
description: 'custom',
|
||||
permissions: ['token-user'],
|
||||
enabled: true,
|
||||
@@ -104,7 +135,7 @@ test('stripBuiltInChatTypes removes built-in chat type ids from saved list', ()
|
||||
},
|
||||
]);
|
||||
|
||||
assert.deepEqual(stripped.map((item) => item.id), ['custom-support-flow', 'plan-checklist-execution']);
|
||||
assert.deepEqual(stripped.map((item) => item.id), ['custom-support-flow', 'general-request', 'plan-checklist-execution']);
|
||||
});
|
||||
|
||||
test('migrateLegacyChatTypeContexts moves legacy plan checklist chat type into default context settings', () => {
|
||||
@@ -124,6 +155,7 @@ test('migrateLegacyChatTypeContexts moves legacy plan checklist chat type into d
|
||||
{
|
||||
id: 'plan-checklist-execution',
|
||||
name: 'Plan 체크리스트 실행',
|
||||
sortOrder: 1,
|
||||
description: 'legacy plan context',
|
||||
permissions: ['token-user'],
|
||||
enabled: true,
|
||||
@@ -178,7 +210,7 @@ test('resolveAppConfigByOrigin falls back to legacy global config when scoped co
|
||||
receiveRoomNotifications: true,
|
||||
},
|
||||
},
|
||||
'https://test.sm-home.cloud',
|
||||
'https://preview.sm-home.cloud',
|
||||
) as {
|
||||
chat?: { receiveRoomNotifications?: boolean };
|
||||
};
|
||||
@@ -208,7 +240,7 @@ test('resolveCanonicalChatContextSettingsFromConfig prefers global chat context
|
||||
],
|
||||
},
|
||||
scopedAppConfigs: {
|
||||
'https://test.sm-home.cloud': {
|
||||
'https://preview.sm-home.cloud': {
|
||||
config: {
|
||||
chatContextSettings: {
|
||||
defaultContexts: [
|
||||
@@ -225,7 +257,7 @@ test('resolveCanonicalChatContextSettingsFromConfig prefers global chat context
|
||||
},
|
||||
},
|
||||
},
|
||||
'https://test.sm-home.cloud',
|
||||
'https://preview.sm-home.cloud',
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
@@ -234,11 +266,76 @@ test('resolveCanonicalChatContextSettingsFromConfig prefers global chat context
|
||||
);
|
||||
});
|
||||
|
||||
test('resolveCanonicalChatContextSettingsFromConfig keeps saved default context sort order and renumbers gaps', () => {
|
||||
const resolved = resolveCanonicalChatContextSettingsFromConfig({
|
||||
chatContextSettings: {
|
||||
defaultContexts: [
|
||||
{
|
||||
id: 'context-b',
|
||||
title: 'B 문맥',
|
||||
sortOrder: 3,
|
||||
content: 'b',
|
||||
enabled: true,
|
||||
updatedAt: '2026-05-08T00:00:00.000Z',
|
||||
},
|
||||
{
|
||||
id: 'context-a',
|
||||
title: 'A 문맥',
|
||||
sortOrder: 1,
|
||||
content: 'a',
|
||||
enabled: true,
|
||||
updatedAt: '2026-05-08T00:00:00.000Z',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
resolved.defaultContexts.map((item) => ({ id: item.id, sortOrder: item.sortOrder })),
|
||||
[
|
||||
{ id: 'context-a', sortOrder: 1 },
|
||||
{ id: 'context-b', sortOrder: 2 },
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
test('resolveCanonicalChatContextSettingsFromConfig appends unsorted default contexts after sorted entries', () => {
|
||||
const resolved = resolveCanonicalChatContextSettingsFromConfig({
|
||||
chatContextSettings: {
|
||||
defaultContexts: [
|
||||
{
|
||||
id: 'context-b',
|
||||
title: 'B 문맥',
|
||||
content: 'b',
|
||||
enabled: true,
|
||||
updatedAt: '2026-05-08T00:00:00.000Z',
|
||||
},
|
||||
{
|
||||
id: 'context-a',
|
||||
title: 'A 문맥',
|
||||
sortOrder: 1,
|
||||
content: 'a',
|
||||
enabled: true,
|
||||
updatedAt: '2026-05-08T00:00:00.000Z',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
resolved.defaultContexts.map((item) => ({ id: item.id, sortOrder: item.sortOrder })),
|
||||
[
|
||||
{ id: 'context-a', sortOrder: 1 },
|
||||
{ id: 'context-b', sortOrder: 2 },
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
test('resolveCanonicalChatContextSettingsFromConfig falls back to scoped settings only when global settings are empty', () => {
|
||||
const resolved = resolveCanonicalChatContextSettingsFromConfig(
|
||||
{
|
||||
scopedAppConfigs: {
|
||||
'https://test.sm-home.cloud': {
|
||||
'https://preview.sm-home.cloud': {
|
||||
config: {
|
||||
chatContextSettings: {
|
||||
defaultContexts: [
|
||||
@@ -255,7 +352,7 @@ test('resolveCanonicalChatContextSettingsFromConfig falls back to scoped setting
|
||||
},
|
||||
},
|
||||
},
|
||||
'https://test.sm-home.cloud',
|
||||
'https://preview.sm-home.cloud',
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
@@ -278,7 +375,7 @@ test('resolveCanonicalChatTypesFromConfig merges global chat types with stale sc
|
||||
},
|
||||
],
|
||||
scopedAppConfigs: {
|
||||
'https://test.sm-home.cloud': {
|
||||
'https://preview.sm-home.cloud': {
|
||||
config: {
|
||||
chatTypes: [
|
||||
{
|
||||
@@ -294,7 +391,7 @@ test('resolveCanonicalChatTypesFromConfig merges global chat types with stale sc
|
||||
},
|
||||
},
|
||||
},
|
||||
'https://test.sm-home.cloud',
|
||||
'https://preview.sm-home.cloud',
|
||||
);
|
||||
|
||||
assert.ok(resolved);
|
||||
@@ -305,7 +402,7 @@ test('resolveCanonicalChatTypesFromConfig merges global chat types with stale sc
|
||||
test('stripChatContextSettingsFromScopedAppConfigs removes stale scoped chat context settings only', () => {
|
||||
const stripped = stripChatContextSettingsFromScopedAppConfigs({
|
||||
scopedAppConfigs: {
|
||||
'https://test.sm-home.cloud': {
|
||||
'https://preview.sm-home.cloud': {
|
||||
config: {
|
||||
chatContextSettings: {
|
||||
defaultContexts: [{ id: 'legacy', title: 'legacy', content: 'legacy', enabled: true }],
|
||||
@@ -321,7 +418,7 @@ test('stripChatContextSettingsFromScopedAppConfigs removes stale scoped chat con
|
||||
|
||||
assert.equal(stripped.changed, true);
|
||||
assert.deepEqual(stripped.scopedConfigs, {
|
||||
'https://test.sm-home.cloud': {
|
||||
'https://preview.sm-home.cloud': {
|
||||
config: {
|
||||
chat: {
|
||||
receiveRoomNotifications: false,
|
||||
@@ -331,3 +428,104 @@ test('stripChatContextSettingsFromScopedAppConfigs removes stale scoped chat con
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('stripSharedContextDataFromScopedAppConfigs removes scoped chat-type/context data and backs up non-shared origins', () => {
|
||||
const stripped = stripSharedContextDataFromScopedAppConfigs(
|
||||
{
|
||||
scopedAppConfigs: {
|
||||
'https://preview.sm-home.cloud': {
|
||||
config: {
|
||||
chatTypes: [
|
||||
{
|
||||
id: 'general-request',
|
||||
name: '일반 요청',
|
||||
description: 'preview-shared',
|
||||
permissions: ['token-user'],
|
||||
enabled: true,
|
||||
updatedAt: '2026-05-08T00:00:00.000Z',
|
||||
},
|
||||
],
|
||||
chatContextSettings: {
|
||||
defaultContexts: [
|
||||
{
|
||||
id: 'preview-context',
|
||||
title: 'preview',
|
||||
content: 'shared',
|
||||
enabled: true,
|
||||
updatedAt: '2026-05-08T00:00:00.000Z',
|
||||
},
|
||||
],
|
||||
},
|
||||
chat: {
|
||||
receiveRoomNotifications: false,
|
||||
},
|
||||
},
|
||||
updatedAt: '2026-05-08T00:00:00.000Z',
|
||||
appDomain: 'preview.sm-home.cloud',
|
||||
},
|
||||
'https://test.sm-home.cloud': {
|
||||
config: {
|
||||
chatTypes: [
|
||||
{
|
||||
id: 'chat-type-test-temp',
|
||||
name: '임시 유형',
|
||||
description: 'test-only',
|
||||
permissions: ['token-user'],
|
||||
enabled: true,
|
||||
updatedAt: '2026-05-08T00:00:00.000Z',
|
||||
},
|
||||
],
|
||||
chatContextSettings: {
|
||||
defaultContexts: [
|
||||
{
|
||||
id: 'test-context',
|
||||
title: 'test',
|
||||
content: 'legacy',
|
||||
enabled: true,
|
||||
updatedAt: '2026-05-08T00:00:00.000Z',
|
||||
},
|
||||
],
|
||||
},
|
||||
automation: {
|
||||
notifyOnAutomationStart: true,
|
||||
},
|
||||
},
|
||||
updatedAt: '2026-05-08T00:00:00.000Z',
|
||||
appDomain: 'test.sm-home.cloud',
|
||||
},
|
||||
},
|
||||
},
|
||||
'https://preview.sm-home.cloud',
|
||||
);
|
||||
|
||||
assert.equal(stripped.changed, true);
|
||||
assert.deepEqual(stripped.scopedConfigs, {
|
||||
'https://preview.sm-home.cloud': {
|
||||
config: {
|
||||
chat: {
|
||||
receiveRoomNotifications: false,
|
||||
},
|
||||
},
|
||||
updatedAt: '2026-05-08T00:00:00.000Z',
|
||||
appDomain: 'preview.sm-home.cloud',
|
||||
},
|
||||
'https://test.sm-home.cloud': {
|
||||
config: {
|
||||
automation: {
|
||||
notifyOnAutomationStart: true,
|
||||
},
|
||||
},
|
||||
updatedAt: '2026-05-08T00:00:00.000Z',
|
||||
appDomain: 'test.sm-home.cloud',
|
||||
},
|
||||
});
|
||||
assert.equal(
|
||||
Array.isArray(stripped.backups['https://test.sm-home.cloud']?.chatTypes),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
stripped.backups['https://test.sm-home.cloud']?.chatContextSettings?.defaultContexts[0]?.id,
|
||||
'test-context',
|
||||
);
|
||||
assert.equal(stripped.backups['https://preview.sm-home.cloud'], undefined);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user