Skip to content

Commit a78bee5

Browse files
committed
feat: add end-to-end tests for global settings initialization and access control
1 parent 587a32a commit a78bee5

File tree

2 files changed

+151
-378
lines changed

2 files changed

+151
-378
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import * as fs from 'fs-extra';
2+
import * as path from 'path';
3+
import Database from 'better-sqlite3';
4+
import type { GlobalSettingsModule, GlobalSettingDefinition } from '../src/global-settings/types';
5+
6+
// Helper function to dynamically get all defined core setting keys
7+
async function getDefinedCoreSettingKeys(): Promise<string[]> {
8+
const definedKeys: string[] = [];
9+
// Correct path from 'services/backend/tests/' to 'services/backend/src/global-settings/'
10+
const globalSettingsDir = path.join(__dirname, '..', 'src', 'global-settings');
11+
12+
try {
13+
const files = fs.readdirSync(globalSettingsDir);
14+
15+
for (const file of files) {
16+
// Process only .ts files, excluding index.ts (auto-discovery service) and types.ts
17+
if (file.endsWith('.ts') && file !== 'index.ts' && file !== 'types.ts') {
18+
const filePath = path.join(globalSettingsDir, file);
19+
try {
20+
const moduleExports = require(filePath); // Dynamically require the .ts file
21+
22+
// Iterate over exports to find the GlobalSettingsModule object
23+
for (const exportName in moduleExports) {
24+
const exportedItem = moduleExports[exportName];
25+
// Check if it structurally resembles a GlobalSettingsModule
26+
if (
27+
exportedItem &&
28+
typeof exportedItem === 'object' &&
29+
exportedItem.group && // Check for group property
30+
typeof exportedItem.group === 'object' &&
31+
Array.isArray(exportedItem.settings) // Check for settings array
32+
) {
33+
const settingsModule = exportedItem as GlobalSettingsModule;
34+
settingsModule.settings.forEach((setting: GlobalSettingDefinition) => {
35+
definedKeys.push(setting.key);
36+
});
37+
// Assuming one main GlobalSettingsModule export per relevant file
38+
break;
39+
}
40+
}
41+
} catch (error) {
42+
console.warn(`Could not load or parse settings from ${file}:`, error);
43+
// Optionally, rethrow or collect errors if critical
44+
}
45+
}
46+
}
47+
} catch (error) {
48+
console.error('Failed to read global-settings directory:', error);
49+
// Rethrow or handle as a test failure if directory read fails
50+
throw error;
51+
}
52+
53+
return [...new Set(definedKeys)]; // Return unique keys
54+
}
55+
56+
describe('Global Settings Initialization Check', () => {
57+
const dbPath = path.join(__dirname, '..', 'persistent_data', 'database', 'deploystack.db');
58+
let db: Database.Database;
59+
60+
beforeAll(() => {
61+
try {
62+
// The database file should exist as '1-setup.e2e.test.ts' must have run and created it.
63+
// Open in read-only mode as this test only verifies existence.
64+
db = new Database(dbPath, { readonly: true, fileMustExist: true });
65+
} catch (error) {
66+
console.error(
67+
`Failed to open database at ${dbPath}. Ensure '1-setup.e2e.test.ts' ran successfully and created the database.`,
68+
error
69+
);
70+
// Re-throw to fail the test suite early if DB connection fails
71+
throw error;
72+
}
73+
});
74+
75+
afterAll(() => {
76+
if (db) {
77+
db.close();
78+
}
79+
});
80+
81+
it('should ensure all defined core global settings are created in the database', async () => {
82+
const definedKeys = await getDefinedCoreSettingKeys();
83+
84+
expect(definedKeys.length).toBeGreaterThanOrEqual(12); // Expect at least smtp (7) + github-oauth (5)
85+
86+
let dbKeys: string[] = [];
87+
let settingsFound = false;
88+
const maxRetries = 10; // Poll for up to 5 seconds (10 * 500ms)
89+
const retryInterval = 500; // 500 ms
90+
91+
for (let i = 0; i < maxRetries; i++) {
92+
try {
93+
const rowsFromDB = db.prepare("SELECT key FROM globalSettings").all() as Array<{ key: string }>;
94+
dbKeys = rowsFromDB.map(row => row.key);
95+
96+
// Check if all defined keys are present
97+
let allFound = true;
98+
// Removed critical log here as the test will fail if settings are not found after polling.
99+
// if (definedKeys.length > 0 && dbKeys.length === 0 && i === 0) {
100+
// console.error('[Test 4] Initial Check CRITICAL: Defined settings were found, but the globalSettings table in the DB is empty. This indicates settings were not initialized into the DB during setup. Polling...');
101+
// }
102+
103+
for (const definedKey of definedKeys) {
104+
if (!dbKeys.includes(definedKey)) {
105+
allFound = false;
106+
break;
107+
}
108+
}
109+
110+
if (allFound && definedKeys.length > 0) { // Ensure definedKeys is not empty
111+
// Additionally, ensure that the number of keys in DB is at least the number of defined keys
112+
if (dbKeys.length >= definedKeys.length) {
113+
settingsFound = true;
114+
// console.log(`[Test 4] All ${definedKeys.length} defined settings found in DB after ${i + 1} attempt(s). DB keys: ${dbKeys.length}`);
115+
break;
116+
}
117+
}
118+
} catch (error) {
119+
console.error('[Test 4] Failed to query globalSettings table during poll:', error);
120+
// If query fails, likely a more significant issue, break and let assertions fail
121+
break;
122+
}
123+
124+
if (i < maxRetries - 1) {
125+
await new Promise(resolve => setTimeout(resolve, retryInterval));
126+
// if (i % 4 === 0) { // Log progress periodically
127+
// console.log(`[Test 4] Retrying DB check for global settings... Attempt ${i + 2}/${maxRetries}`);
128+
// }
129+
}
130+
}
131+
132+
// console.log('[Test 4] Final keys found in DB globalSettings table after polling:', dbKeys.length, dbKeys);
133+
134+
if (!settingsFound && definedKeys.length > 0) {
135+
console.error('[Test 4] FAILURE: After polling, not all defined global settings were found in the database.');
136+
}
137+
138+
// Perform final assertions
139+
for (const definedKey of definedKeys) {
140+
expect(dbKeys).toContain(definedKey);
141+
}
142+
143+
// Optional: A stricter check to ensure no unexpected keys are present.
144+
// This ensures that if settingsFound is true, the dbKeys array actually contains all definedKeys.
145+
// This assumes that only core settings should be in the DB at this stage of testing
146+
// and plugins (out of scope for this test) haven't added any.
147+
// For now, we'll stick to ensuring all defined keys are present.
148+
// expect(dbKeys.length).toEqual(definedKeys.length);
149+
// expect(dbKeys.sort()).toEqual(definedKeys.sort()); // Even stricter: exact match
150+
});
151+
});

0 commit comments

Comments
 (0)