124 lines
4.0 KiB
JavaScript
124 lines
4.0 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { readFile } from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
import test from 'node:test';
|
|
import {
|
|
assertMissing,
|
|
configModuleUrl,
|
|
createFixture,
|
|
} from '../test-support/droid-test-helpers.mjs';
|
|
|
|
test('Droid resolves hooksDisabled through official settings precedence', async (t) => {
|
|
await t.test('user settings disable installation before runtime creation', async () => {
|
|
const fixture = await createFixture({ settings: { hooksDisabled: true } });
|
|
try {
|
|
const result = await fixture.install();
|
|
assert.equal(result.code, 1);
|
|
assert.match(result.stderr, /hooksDisabled/);
|
|
await assertMissing(path.join(fixture.agentDir, 'config.json'));
|
|
await assertMissing(fixture.rootPath);
|
|
} finally {
|
|
await fixture.close();
|
|
}
|
|
});
|
|
|
|
await t.test('user local settings override their base file', async () => {
|
|
const fixture = await createFixture({
|
|
settings: { hooksDisabled: true },
|
|
localSettings: { hooksDisabled: false },
|
|
});
|
|
try {
|
|
assert.equal((await fixture.install()).code, 0);
|
|
} finally {
|
|
await fixture.close();
|
|
}
|
|
});
|
|
|
|
await t.test('legacy direct flags remain safety-compatible', async () => {
|
|
const fixture = await createFixture({
|
|
legacyConfig: { hooksDisabled: true, PreToolUse: [] },
|
|
settings: { hooksDisabled: false },
|
|
});
|
|
try {
|
|
const result = await fixture.install();
|
|
assert.equal(result.code, 1);
|
|
assert.match(result.stderr, new RegExp(fixture.legacyPath.replaceAll('\\', '\\\\'), 'i'));
|
|
} finally {
|
|
await fixture.close();
|
|
}
|
|
});
|
|
|
|
await t.test('project policy takes precedence over user settings', async () => {
|
|
const fixture = await createFixture({
|
|
settings: { hooksDisabled: false },
|
|
projectSettings: { hooksDisabled: true },
|
|
});
|
|
try {
|
|
const result = await fixture.install();
|
|
assert.equal(result.code, 1);
|
|
assert.match(result.stderr, /project settings/);
|
|
await assertMissing(path.join(fixture.agentDir, 'config.json'));
|
|
} finally {
|
|
await fixture.close();
|
|
}
|
|
});
|
|
|
|
await t.test('project false locks out a lower user disable flag', async () => {
|
|
const fixture = await createFixture({
|
|
settings: { hooksDisabled: true },
|
|
projectSettings: { hooksDisabled: false },
|
|
});
|
|
try {
|
|
assert.equal((await fixture.install()).code, 0);
|
|
} finally {
|
|
await fixture.close();
|
|
}
|
|
});
|
|
|
|
await t.test('project local settings override their base file', async () => {
|
|
const fixture = await createFixture({
|
|
projectSettings: { hooksDisabled: false },
|
|
projectLocalSettings: { hooksDisabled: true },
|
|
});
|
|
try {
|
|
const result = await fixture.install();
|
|
assert.equal(result.code, 1);
|
|
assert.match(result.stderr, /project local settings/);
|
|
} finally {
|
|
await fixture.close();
|
|
}
|
|
});
|
|
});
|
|
|
|
test('Droid treats allowManagedHooksOnly as an authoritative user-hook block', async () => {
|
|
const { hookBlock } = await import(configModuleUrl);
|
|
assert.deepEqual(hookBlock({
|
|
policy: {
|
|
allowManagedHooksOnlyBy: {
|
|
filePath: '/managed/settings.json',
|
|
label: 'Factory Droid system-managed settings',
|
|
},
|
|
preconditions: [],
|
|
},
|
|
}), {
|
|
field: 'allowManagedHooksOnly',
|
|
filePath: '/managed/settings.json',
|
|
label: 'Factory Droid system-managed settings',
|
|
});
|
|
});
|
|
|
|
test('Droid surfaces malformed read-only project policy before any write', async () => {
|
|
const fixture = await createFixture({ projectSettings: '{ malformed' });
|
|
try {
|
|
const before = await readFile(fixture.projectSettingsPath, 'utf-8');
|
|
const result = await fixture.install();
|
|
assert.equal(result.code, 1);
|
|
assert.match(result.stderr, /project settings/);
|
|
assert.equal(await readFile(fixture.projectSettingsPath, 'utf-8'), before);
|
|
await assertMissing(path.join(fixture.agentDir, 'config.json'));
|
|
await assertMissing(fixture.rootPath);
|
|
} finally {
|
|
await fixture.close();
|
|
}
|
|
});
|