mirror of
https://github.com/qwibitai/nanoclaw.git
synced 2026-06-04 10:14:47 +08:00
5dc54194ab
The credential proxy already reads ANTHROPIC_AUTH_TOKEN (credential-proxy.ts line 33) and uses it for OAuth-mode authentication, but setup/verify.ts did not include it in its credential-detection regex. Users with ANTHROPIC_AUTH_TOKEN in .env saw 'CREDENTIALS: missing' even though their credentials were valid at runtime. Add ANTHROPIC_AUTH_TOKEN to the regex and add a matching test case. Closes gh-853
136 lines
4.5 KiB
TypeScript
136 lines
4.5 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
import fs from 'fs';
|
|
import os from 'os';
|
|
import path from 'path';
|
|
|
|
import Database from 'better-sqlite3';
|
|
|
|
/**
|
|
* Tests for the environment check step.
|
|
*
|
|
* Verifies: config detection, Docker/AC detection, DB queries.
|
|
*/
|
|
|
|
describe('environment detection', () => {
|
|
it('detects platform correctly', async () => {
|
|
const { getPlatform } = await import('./platform.js');
|
|
const platform = getPlatform();
|
|
expect(['macos', 'linux', 'unknown']).toContain(platform);
|
|
});
|
|
});
|
|
|
|
describe('detectRegisteredGroups', () => {
|
|
let tempDir: string;
|
|
|
|
beforeEach(() => {
|
|
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nanoclaw-env-test-'));
|
|
fs.mkdirSync(path.join(tempDir, 'data'), { recursive: true });
|
|
});
|
|
|
|
afterEach(() => {
|
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
});
|
|
|
|
it('returns false when no registration state exists', async () => {
|
|
const { detectRegisteredGroups } = await import('./environment.js');
|
|
expect(detectRegisteredGroups(tempDir)).toBe(false);
|
|
});
|
|
|
|
it('detects pre-migration registered_groups.json', async () => {
|
|
const { detectRegisteredGroups } = await import('./environment.js');
|
|
fs.writeFileSync(path.join(tempDir, 'data', 'registered_groups.json'), '[]');
|
|
expect(detectRegisteredGroups(tempDir)).toBe(true);
|
|
});
|
|
|
|
it('returns false for an empty v2 central DB', async () => {
|
|
const { detectRegisteredGroups } = await import('./environment.js');
|
|
const db = new Database(path.join(tempDir, 'data', 'v2.db'));
|
|
db.exec(`
|
|
CREATE TABLE agent_groups (id TEXT PRIMARY KEY);
|
|
CREATE TABLE messaging_group_agents (
|
|
id TEXT PRIMARY KEY,
|
|
messaging_group_id TEXT NOT NULL,
|
|
agent_group_id TEXT NOT NULL
|
|
);
|
|
`);
|
|
db.close();
|
|
|
|
expect(detectRegisteredGroups(tempDir)).toBe(false);
|
|
});
|
|
|
|
it('detects wired agent groups in the v2 central DB', async () => {
|
|
const { detectRegisteredGroups } = await import('./environment.js');
|
|
const db = new Database(path.join(tempDir, 'data', 'v2.db'));
|
|
db.exec(`
|
|
CREATE TABLE agent_groups (id TEXT PRIMARY KEY);
|
|
CREATE TABLE messaging_group_agents (
|
|
id TEXT PRIMARY KEY,
|
|
messaging_group_id TEXT NOT NULL,
|
|
agent_group_id TEXT NOT NULL
|
|
);
|
|
`);
|
|
db.prepare('INSERT INTO agent_groups (id) VALUES (?)').run('ag-1');
|
|
db.prepare(
|
|
'INSERT INTO messaging_group_agents (id, messaging_group_id, agent_group_id) VALUES (?, ?, ?)',
|
|
).run('mga-1', 'mg-1', 'ag-1');
|
|
db.close();
|
|
|
|
expect(detectRegisteredGroups(tempDir)).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('credentials detection', () => {
|
|
it('detects ANTHROPIC_API_KEY in env content', () => {
|
|
const content =
|
|
'SOME_KEY=value\nANTHROPIC_API_KEY=sk-ant-test123\nOTHER=foo';
|
|
const hasCredentials =
|
|
/^(CLAUDE_CODE_OAUTH_TOKEN|ANTHROPIC_API_KEY|ANTHROPIC_AUTH_TOKEN|ONECLI_URL)=/m.test(content);
|
|
expect(hasCredentials).toBe(true);
|
|
});
|
|
|
|
it('detects CLAUDE_CODE_OAUTH_TOKEN in env content', () => {
|
|
const content = 'CLAUDE_CODE_OAUTH_TOKEN=token123';
|
|
const hasCredentials =
|
|
/^(CLAUDE_CODE_OAUTH_TOKEN|ANTHROPIC_API_KEY|ANTHROPIC_AUTH_TOKEN|ONECLI_URL)=/m.test(content);
|
|
expect(hasCredentials).toBe(true);
|
|
});
|
|
|
|
it('detects ANTHROPIC_AUTH_TOKEN in env content', () => {
|
|
const content = 'ANTHROPIC_AUTH_TOKEN=token123\nANTHROPIC_BASE_URL=http://localhost:8080';
|
|
const hasCredentials =
|
|
/^(CLAUDE_CODE_OAUTH_TOKEN|ANTHROPIC_API_KEY|ANTHROPIC_AUTH_TOKEN|ONECLI_URL)=/m.test(content);
|
|
expect(hasCredentials).toBe(true);
|
|
});
|
|
|
|
it('returns false when no credentials', () => {
|
|
const content = 'ASSISTANT_NAME="Andy"\nOTHER=foo';
|
|
const hasCredentials =
|
|
/^(CLAUDE_CODE_OAUTH_TOKEN|ANTHROPIC_API_KEY|ANTHROPIC_AUTH_TOKEN|ONECLI_URL)=/m.test(content);
|
|
expect(hasCredentials).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('Docker detection logic', () => {
|
|
it('commandExists returns boolean', async () => {
|
|
const { commandExists } = await import('./platform.js');
|
|
expect(typeof commandExists('docker')).toBe('boolean');
|
|
expect(typeof commandExists('nonexistent_binary_xyz')).toBe('boolean');
|
|
});
|
|
});
|
|
|
|
describe('channel auth detection', () => {
|
|
it('detects non-empty auth directory', () => {
|
|
const hasAuth = (authDir: string) => {
|
|
try {
|
|
return fs.existsSync(authDir) && fs.readdirSync(authDir).length > 0;
|
|
} catch {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// Non-existent directory
|
|
expect(hasAuth('/tmp/nonexistent_auth_dir_xyz')).toBe(false);
|
|
});
|
|
});
|
|
|