rowboat/GITHUB_COPILOT_E2E_TESTING.md
Rowboat Developer e1e6fa92e8 docs: Add comprehensive end-to-end testing guide for GitHub Copilot
- Add step-by-step testing procedures for all components
- Include manual Device Flow authentication testing
- Add LLM provider testing examples
- Include token storage and refresh verification
- Add model discovery and disconnection tests
- Provide troubleshooting guide
- Add security notes and best practices
2026-04-17 09:23:21 -05:00

280 lines
7.7 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# GitHub Copilot Integration - End-to-End Testing Guide
Este documento describe cómo realizar pruebas end-to-end completas de la integración de GitHub Copilot en Rowboat.
## Requisitos Previos
- Rowboat compilado y funcionando (`npm run deps` sin errores)
- Cuenta de GitHub activa
- Acceso a GitHub Copilot (Student, Pro, o Enterprise)
## Test 1: Verificar Compilación
```bash
cd /home/wilber/rowboat/apps/x
# Compilar todas las dependencias
npm run deps
# Verificar que no hay errores de TypeScript
npm run lint
# Expected output:
# ✓ shared compiled successfully
# ✓ core compiled successfully
# ✓ preload compiled successfully
```
## Test 2: Verificar Tests Unitarios
```bash
# Ejecutar tests de GitHub Copilot
npm test -- github-copilot.test.ts
# Expected output:
# ✓ GitHub Copilot Device Flow
# ✓ requestDeviceCode
# ✓ pollForToken
# ✓ startGitHubCopilotAuth
# ✓ OAuthTokens validation
# ✓ GitHub Copilot Models
# ✓ Model availability
# ✓ Model constants
#
# Tests: 25+ passed
```
## Test 3: Device Flow Authentication (Manual)
### Paso 1: Crear un script de prueba
Crea `/tmp/test-github-copilot-auth.ts`:
```typescript
import container from '@x/core/di/container';
import { startGitHubCopilotAuthentication, isGitHubCopilotAuthenticated, getGitHubCopilotAuthStatus } from '@x/core/auth/github-copilot-auth';
async function testAuth() {
console.log('Starting GitHub Copilot authentication test...\n');
// Paso 1: Iniciar autenticación
console.log('1⃣ Iniciando Device Flow...');
const { userCode, verificationUri, tokenPromise } = await startGitHubCopilotAuthentication();
console.log(`\n📱 Código de dispositivo: ${userCode}`);
console.log(`🔗 Visita: ${verificationUri}`);
console.log('\n⏳ Esperando autorización... (timeout en 15 minutos)\n');
try {
// Paso 2: Esperar autenticación
await tokenPromise;
console.log('✅ ¡Autenticado exitosamente!\n');
// Paso 3: Verificar estado
const authenticated = await isGitHubCopilotAuthenticated();
console.log(`2⃣ ¿Autenticado? ${authenticated ? '✅ Sí' : '❌ No'}`);
const status = await getGitHubCopilotAuthStatus();
console.log(`3⃣ Estado:`, JSON.stringify(status, null, 2));
console.log('\n✨ Test completo exitosamente');
} catch (error) {
console.error('\n❌ Error:', error);
process.exit(1);
}
}
testAuth();
```
### Paso 2: Ejecutar el test
```bash
cd /home/wilber/rowboat/apps/x
npx ts-node /tmp/test-github-copilot-auth.ts
# Expected output:
# 1⃣ Iniciando Device Flow...
#
# 📱 Código de dispositivo: ABCD-1234
# 🔗 Visita: https://github.com/login/device
#
# ⏳ Esperando autorización... (timeout en 15 minutos)
#
# (Usuario visita GitHub, ingresa código ABCD-1234)
#
# ✅ ¡Autenticado exitosamente!
#
# 2⃣ ¿Autenticado? ✅ Sí
# 3⃣ Estado: {
# "authenticated": true,
# "expiresAt": 1234567890
# }
#
# ✨ Test completo exitosamente
```
## Test 4: Crear proveedor LLM
```typescript
import { createProvider } from '@x/core/models/models';
import { generateText } from 'ai';
async function testLLM() {
console.log('Testing GitHub Copilot LLM...\n');
// Crear proveedor
console.log('1⃣ Creando proveedor GitHub Copilot...');
const config = {
flavor: 'github-copilot' as const,
};
const provider = await createProvider(config);
console.log('✅ Proveedor creado\n');
// Crear modelo
console.log('2⃣ Creando modelo gpt-4o...');
const model = provider.languageModel('gpt-4o');
console.log('✅ Modelo creado\n');
// Generar texto
console.log('3⃣ Enviando prompt a GitHub Copilot...');
const response = await generateText({
model,
prompt: 'Say hello in Spanish',
});
console.log('✅ Respuesta recibida:\n');
console.log(response.text);
}
testLLM();
```
## Test 5: Verificar Almacenamiento de Tokens
```bash
# Ver tokens guardados
cat ~/.rowboat/config/oauth.json | jq '.providers."github-copilot"'
# Expected output:
# {
# "tokens": {
# "access_token": "ghu_...",
# "refresh_token": null,
# "expires_at": 1234567890,
# "token_type": "Bearer",
# "scopes": ["read:user", "user:email", "gist"]
# },
# "clientId": "Iv1.b507a08c87ecfe98"
# }
```
## Test 6: Probar Refresh de Tokens
```typescript
import { getGitHubCopilotAccessToken } from '@x/core/auth/github-copilot-auth';
import * as oauthClient from '@x/core/auth/oauth-client';
async function testTokenRefresh() {
console.log('Testing token refresh...\n');
// Obtener token actual
console.log('1⃣ Obteniendo token de acceso...');
const token = await getGitHubCopilotAccessToken();
console.log(`✅ Token: ${token.substring(0, 20)}...\n`);
// Verificar expiración
console.log('2⃣ Verificando expiración...');
const connection = await container.resolve('oauthRepo').read('github-copilot');
if (connection.tokens) {
const expiresIn = connection.tokens.expires_at - Math.floor(Date.now() / 1000);
console.log(`✅ Token expira en: ${expiresIn} segundos`);
if (expiresIn > 3600) {
console.log(' (Aún es válido por más de 1 hora)\n');
}
}
}
testTokenRefresh();
```
## Test 7: Listar Modelos Disponibles
```typescript
import { getAvailableGitHubCopilotModels } from '@x/core/auth/github-copilot-models';
async function testModels() {
console.log('Modelos disponibles en GitHub Copilot:\n');
const models = await getAvailableGitHubCopilotModels();
models.forEach((model, i) => {
console.log(`${i + 1}. ${model}`);
});
}
testModels();
```
## Test 8: Desconectar GitHub Copilot
```typescript
import { disconnectGitHubCopilot, isGitHubCopilotAuthenticated } from '@x/core/auth/github-copilot-auth';
async function testDisconnect() {
console.log('Desconectando GitHub Copilot...\n');
console.log('1⃣ Estado antes: ', await isGitHubCopilotAuthenticated());
await disconnectGitHubCopilot();
console.log('2⃣ Estado después: ', await isGitHubCopilotAuthenticated());
console.log('\n✅ Desconectado correctamente');
}
testDisconnect();
```
## Verificación de Checklist
- [ ] Compilación exitosa sin errores TypeScript
- [ ] Tests unitarios pasan (25+ casos)
- [ ] Device Flow funciona y abre el navegador
- [ ] Usuario puede completar autenticación en GitHub
- [ ] Tokens se guardan en `~/.rowboat/config/oauth.json`
- [ ] Proveedor LLM se crea correctamente
- [ ] Modelo responde a prompts
- [ ] Token se actualiza automáticamente si expira
- [ ] Tokens se eliminan al desconectar
- [ ] Modelos disponibles se listan correctamente
## Troubleshooting
### Error: "GitHub Copilot not authenticated"
- Ejecutar Device Flow nuevamente: `startGitHubCopilotAuthentication()`
- Verificar que tokens existen: `cat ~/.rowboat/config/oauth.json`
### Error: "Token expired"
- El sistema debería intentar refresh automático
- Si falla, ejecutar Device Flow nuevamente
### Error: "Cannot reach API"
- Verificar conexión a internet
- Verificar que `https://models.github.com/api/openai/` es accesible
- Verificar que token es válido: `npm run test -- github-copilot`
### Error: "Model not found"
- Verificar que el modelo está disponible en tu plan
- Usar `gpt-4o` como fallback
## Recursos Adicionales
- [GITHUB_COPILOT_INTEGRATION.md](./GITHUB_COPILOT_INTEGRATION.md) - Documentación técnica completa
- [RFC 8628](https://tools.ietf.org/html/rfc8628) - Device Flow OAuth spec
- [GitHub Copilot Docs](https://docs.github.com/en/copilot) - Documentación oficial
## Notas de Seguridad
- **Nunca** compartas tu código de dispositivo
- Los tokens se almacenan en `~/.rowboat/config/oauth.json` - asegúrate de que los permisos son correctos
- Desconecta cuando no uses GitHub Copilot
- Los tokens expiran automáticamente (generalmente en 8 horas)