test pds oauth did

This commit is contained in:
2025-06-17 10:39:48 +09:00
parent 0d79af5aa5
commit 6600a9e0cf
18 changed files with 589 additions and 137 deletions

View File

@@ -0,0 +1,135 @@
// Simple console test for OAuth app
// This runs before 'npm run preview' to display test results
// Mock import.meta.env for Node.js environment
(global as any).import = {
meta: {
env: {
VITE_ATPROTO_PDS: process.env.VITE_ATPROTO_PDS || 'syu.is',
VITE_ADMIN_HANDLE: process.env.VITE_ADMIN_HANDLE || 'ai.syui.ai',
VITE_AI_HANDLE: process.env.VITE_AI_HANDLE || 'ai.syui.ai',
VITE_OAUTH_COLLECTION: process.env.VITE_OAUTH_COLLECTION || 'ai.syui.log',
VITE_ATPROTO_HANDLE_LIST: process.env.VITE_ATPROTO_HANDLE_LIST || '["syui.ai", "ai.syui.ai", "yui.syui.ai"]',
VITE_APP_HOST: process.env.VITE_APP_HOST || 'https://log.syui.ai'
}
}
};
// Simple implementation of functions for testing
function detectPdsFromHandle(handle: string): string {
if (handle.endsWith('.syu.is') || handle.endsWith('.syui.ai')) {
return 'syu.is';
}
if (handle.endsWith('.bsky.social')) {
return 'bsky.social';
}
// Default case - check if it's in the allowed list
const allowedHandles = JSON.parse((global as any).import.meta.env.VITE_ATPROTO_HANDLE_LIST || '[]');
if (allowedHandles.includes(handle)) {
return (global as any).import.meta.env.VITE_ATPROTO_PDS || 'syu.is';
}
return 'bsky.social';
}
function getNetworkConfig(pds: string) {
switch (pds) {
case 'bsky.social':
case 'bsky.app':
return {
pdsApi: `https://${pds}`,
plcApi: 'https://plc.directory',
bskyApi: 'https://public.api.bsky.app',
webUrl: 'https://bsky.app'
};
case 'syu.is':
return {
pdsApi: 'https://syu.is',
plcApi: 'https://plc.syu.is',
bskyApi: 'https://bsky.syu.is',
webUrl: 'https://web.syu.is'
};
default:
return {
pdsApi: `https://${pds}`,
plcApi: 'https://plc.directory',
bskyApi: 'https://public.api.bsky.app',
webUrl: 'https://bsky.app'
};
}
}
// Main test execution
console.log('\n=== OAuth App Configuration Tests ===\n');
// Test 1: Handle input behavior
console.log('1. Handle Input → PDS Detection:');
const testHandles = [
'syui.ai',
'syui.syu.is',
'syui.syui.ai',
'test.bsky.social',
'unknown.handle'
];
testHandles.forEach(handle => {
const pds = detectPdsFromHandle(handle);
const config = getNetworkConfig(pds);
console.log(` ${handle.padEnd(20)} → PDS: ${pds.padEnd(12)} → API: ${config.pdsApi}`);
});
// Test 2: Environment variable impact
console.log('\n2. Current Environment Configuration:');
const env = (global as any).import.meta.env;
console.log(` VITE_ATPROTO_PDS: ${env.VITE_ATPROTO_PDS}`);
console.log(` VITE_ADMIN_HANDLE: ${env.VITE_ADMIN_HANDLE}`);
console.log(` VITE_AI_HANDLE: ${env.VITE_AI_HANDLE}`);
console.log(` VITE_OAUTH_COLLECTION: ${env.VITE_OAUTH_COLLECTION}`);
console.log(` VITE_ATPROTO_HANDLE_LIST: ${env.VITE_ATPROTO_HANDLE_LIST}`);
// Test 3: API endpoint generation
console.log('\n3. Generated API Endpoints:');
const adminPds = detectPdsFromHandle(env.VITE_ADMIN_HANDLE);
const adminConfig = getNetworkConfig(adminPds);
console.log(` Admin PDS detection: ${env.VITE_ADMIN_HANDLE}${adminPds}`);
console.log(` Admin API endpoints:`);
console.log(` - PDS API: ${adminConfig.pdsApi}`);
console.log(` - Bsky API: ${adminConfig.bskyApi}`);
console.log(` - Web URL: ${adminConfig.webUrl}`);
// Test 4: Collection URLs
console.log('\n4. Collection API URLs:');
const baseCollection = env.VITE_OAUTH_COLLECTION;
console.log(` User list: ${adminConfig.pdsApi}/xrpc/com.atproto.repo.listRecords?repo=${env.VITE_ADMIN_HANDLE}&collection=${baseCollection}.user`);
console.log(` Chat: ${adminConfig.pdsApi}/xrpc/com.atproto.repo.listRecords?repo=${env.VITE_ADMIN_HANDLE}&collection=${baseCollection}.chat`);
console.log(` Lang: ${adminConfig.pdsApi}/xrpc/com.atproto.repo.listRecords?repo=${env.VITE_ADMIN_HANDLE}&collection=${baseCollection}.chat.lang`);
console.log(` Comment: ${adminConfig.pdsApi}/xrpc/com.atproto.repo.listRecords?repo=${env.VITE_ADMIN_HANDLE}&collection=${baseCollection}.chat.comment`);
// Test 5: OAuth routing logic
console.log('\n5. OAuth Authorization Logic:');
const allowedHandles = JSON.parse(env.VITE_ATPROTO_HANDLE_LIST || '[]');
console.log(` Allowed handles: ${JSON.stringify(allowedHandles)}`);
console.log(` OAuth scenarios:`);
const oauthTestCases = [
'syui.ai', // Should use syu.is (in allowed list)
'test.syu.is', // Should use syu.is (*.syu.is pattern)
'user.bsky.social' // Should use bsky.social (default)
];
oauthTestCases.forEach(handle => {
const pds = detectPdsFromHandle(handle);
const isAllowed = allowedHandles.includes(handle);
const reason = handle.endsWith('.syu.is') ? '*.syu.is pattern' :
isAllowed ? 'in allowed list' :
'default';
console.log(` ${handle.padEnd(20)} → https://${pds}/oauth/authorize (${reason})`);
});
// Test 6: AI Profile Resolution
console.log('\n6. AI Profile Resolution:');
const aiPds = detectPdsFromHandle(env.VITE_AI_HANDLE);
const aiConfig = getNetworkConfig(aiPds);
console.log(` AI Handle: ${env.VITE_AI_HANDLE} → PDS: ${aiPds}`);
console.log(` AI Profile API: ${aiConfig.bskyApi}/xrpc/app.bsky.actor.getProfile?actor=${env.VITE_AI_HANDLE}`);
console.log('\n=== Tests Complete ===\n');

View File

@@ -0,0 +1,141 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { getAppConfig } from '../config/app';
import { detectPdsFromHandle, getNetworkConfig } from '../App';
// Test helper to mock environment variables
const mockEnv = (vars: Record<string, string>) => {
Object.keys(vars).forEach(key => {
(import.meta.env as any)[key] = vars[key];
});
};
describe('OAuth App Tests', () => {
describe('Handle Input Behavior', () => {
it('should detect PDS for syui.ai (Bluesky)', () => {
const pds = detectPdsFromHandle('syui.ai');
expect(pds).toBe('bsky.social');
});
it('should detect PDS for syui.syu.is (syu.is)', () => {
const pds = detectPdsFromHandle('syui.syu.is');
expect(pds).toBe('syu.is');
});
it('should detect PDS for syui.syui.ai (syu.is)', () => {
const pds = detectPdsFromHandle('syui.syui.ai');
expect(pds).toBe('syu.is');
});
it('should use network config for different PDS', () => {
const bskyConfig = getNetworkConfig('bsky.social');
expect(bskyConfig.pdsApi).toBe('https://bsky.social');
expect(bskyConfig.bskyApi).toBe('https://public.api.bsky.app');
expect(bskyConfig.webUrl).toBe('https://bsky.app');
const syuisConfig = getNetworkConfig('syu.is');
expect(syuisConfig.pdsApi).toBe('https://syu.is');
expect(syuisConfig.bskyApi).toBe('https://bsky.syu.is');
expect(syuisConfig.webUrl).toBe('https://web.syu.is');
});
});
describe('Environment Variable Changes', () => {
beforeEach(() => {
// Reset environment variables
delete (import.meta.env as any).VITE_ATPROTO_PDS;
delete (import.meta.env as any).VITE_ADMIN_HANDLE;
delete (import.meta.env as any).VITE_AI_HANDLE;
});
it('should use correct PDS for AI profile', () => {
mockEnv({
VITE_ATPROTO_PDS: 'syu.is',
VITE_ADMIN_HANDLE: 'ai.syui.ai',
VITE_AI_HANDLE: 'ai.syui.ai'
});
const config = getAppConfig();
expect(config.atprotoPds).toBe('syu.is');
expect(config.adminHandle).toBe('ai.syui.ai');
expect(config.aiHandle).toBe('ai.syui.ai');
// Network config should use syu.is endpoints
const networkConfig = getNetworkConfig(config.atprotoPds);
expect(networkConfig.bskyApi).toBe('https://bsky.syu.is');
});
it('should construct correct API requests for admin userlist', () => {
mockEnv({
VITE_ATPROTO_PDS: 'syu.is',
VITE_ADMIN_HANDLE: 'ai.syui.ai',
VITE_OAUTH_COLLECTION: 'ai.syui.log'
});
const config = getAppConfig();
const networkConfig = getNetworkConfig(config.atprotoPds);
const userListUrl = `${networkConfig.pdsApi}/xrpc/com.atproto.repo.listRecords?repo=${config.adminHandle}&collection=${config.collections.base}.user`;
expect(userListUrl).toBe('https://syu.is/xrpc/com.atproto.repo.listRecords?repo=ai.syui.ai&collection=ai.syui.log.user');
});
});
describe('OAuth Login Flow', () => {
it('should use syu.is OAuth for handles in VITE_ATPROTO_HANDLE_LIST', () => {
mockEnv({
VITE_ATPROTO_HANDLE_LIST: '["syui.ai", "ai.syui.ai", "yui.syui.ai"]',
VITE_ATPROTO_PDS: 'syu.is'
});
const config = getAppConfig();
const handle = 'syui.ai';
// Check if handle is in allowed list
expect(config.allowedHandles).toContain(handle);
// Should use configured PDS for OAuth
const expectedAuthUrl = `https://${config.atprotoPds}/oauth/authorize`;
expect(expectedAuthUrl).toContain('syu.is');
});
it('should use syu.is OAuth for *.syu.is handles', () => {
const handle = 'test.syu.is';
const pds = detectPdsFromHandle(handle);
expect(pds).toBe('syu.is');
});
});
});
// Terminal display test output
export function runTerminalTests() {
console.log('\n=== OAuth App Tests ===\n');
// Test 1: Handle input behavior
console.log('1. Handle Input Detection:');
const handles = ['syui.ai', 'syui.syu.is', 'syui.syui.ai'];
handles.forEach(handle => {
const pds = detectPdsFromHandle(handle);
console.log(` ${handle} → PDS: ${pds}`);
});
// Test 2: Environment variable impact
console.log('\n2. Environment Variables:');
const config = getAppConfig();
console.log(` VITE_ATPROTO_PDS: ${config.atprotoPds}`);
console.log(` VITE_ADMIN_HANDLE: ${config.adminHandle}`);
console.log(` VITE_AI_HANDLE: ${config.aiHandle}`);
console.log(` VITE_OAUTH_COLLECTION: ${config.collections.base}`);
// Test 3: API endpoints
console.log('\n3. API Endpoints:');
const networkConfig = getNetworkConfig(config.atprotoPds);
console.log(` Admin PDS API: ${networkConfig.pdsApi}`);
console.log(` Admin Bsky API: ${networkConfig.bskyApi}`);
console.log(` User list URL: ${networkConfig.pdsApi}/xrpc/com.atproto.repo.listRecords?repo=${config.adminHandle}&collection=${config.collections.base}.user`);
// Test 4: OAuth routing
console.log('\n4. OAuth Routing:');
console.log(` Allowed handles: ${JSON.stringify(config.allowedHandles)}`);
console.log(` OAuth endpoint: https://${config.atprotoPds}/oauth/authorize`);
console.log('\n=== End Tests ===\n');
}