Files
google-drive-content-adapter/tests/unit/auth.test.js

257 lines
8.9 KiB
JavaScript

/**
* Unit Tests: Service Account Authentication
*
* Tests T033-T034: Test JWT authentication and credential validation
* Tests the auth.js module in isolation
*
* @module tests/unit/auth
*/
import { describe, it, beforeEach } from 'node:test';
import assert from 'node:assert/strict';
// =============================================================================
// T033: Unit test for Service Account JWT authentication
// =============================================================================
describe('T033: Service Account JWT Authentication', () => {
let originalEnv;
beforeEach(() => {
// Save original env
originalEnv = process.env.GOOGLE_SERVICE_ACCOUNT_KEY;
});
it('should create GoogleAuth client from GOOGLE_SERVICE_ACCOUNT_KEY env var', async () => {
// Mock credentials as inline JSON (per clarification #1)
const mockCredentials = {
type: 'service_account',
project_id: 'test-project',
private_key_id: 'key123',
private_key: '-----BEGIN PRIVATE KEY-----\nMOCK_KEY\n-----END PRIVATE KEY-----\n',
client_email: 'test@test-project.iam.gserviceaccount.com',
client_id: '123456789',
auth_uri: 'https://accounts.google.com/o/oauth2/auth',
token_uri: 'https://oauth2.googleapis.com/token',
auth_provider_x509_cert_url: 'https://www.googleapis.com/oauth2/v1/certs'
};
// Set env var with inline JSON
process.env.GOOGLE_SERVICE_ACCOUNT_KEY = JSON.stringify(mockCredentials);
// TODO: Import and call initializeAuth from src/auth.js
// const { initializeAuth } = await import('../../src/auth.js');
// const auth = await initializeAuth();
// Verify GoogleAuth was created with correct credentials
// assert.ok(auth, 'Should return auth client');
// assert.equal(auth.credentials.client_email, mockCredentials.client_email, 'Should use client_email from env var');
// Restore env
if (originalEnv) {
process.env.GOOGLE_SERVICE_ACCOUNT_KEY = originalEnv;
} else {
delete process.env.GOOGLE_SERVICE_ACCOUNT_KEY;
}
});
it('should use correct Drive API scope (read-only)', async () => {
const mockCredentials = {
type: 'service_account',
project_id: 'test-project',
private_key: '-----BEGIN PRIVATE KEY-----\nMOCK_KEY\n-----END PRIVATE KEY-----\n',
client_email: 'test@test-project.iam.gserviceaccount.com'
};
process.env.GOOGLE_SERVICE_ACCOUNT_KEY = JSON.stringify(mockCredentials);
// TODO: Import and call initializeAuth
// const { initializeAuth } = await import('../../src/auth.js');
// const auth = await initializeAuth();
// Verify scope is read-only
const expectedScope = 'https://www.googleapis.com/auth/drive.readonly';
// assert.ok(auth.scopes.includes(expectedScope), 'Should use drive.readonly scope');
// Restore env
if (originalEnv) {
process.env.GOOGLE_SERVICE_ACCOUNT_KEY = originalEnv;
} else {
delete process.env.GOOGLE_SERVICE_ACCOUNT_KEY;
}
});
it('should parse inline JSON from env var correctly', async () => {
// Test with different JSON formatting (whitespace, escaped quotes)
const mockCredentials = {
client_email: 'test@project.iam.gserviceaccount.com',
private_key: '-----BEGIN PRIVATE KEY-----\nMOCK_KEY\n-----END PRIVATE KEY-----\n',
project_id: 'test-project'
};
// Set with extra whitespace
process.env.GOOGLE_SERVICE_ACCOUNT_KEY = JSON.stringify(mockCredentials, null, 2);
// TODO: Import and call initializeAuth
// const { initializeAuth } = await import('../../src/auth.js');
// const auth = await initializeAuth();
// Should parse correctly despite formatting
// assert.ok(auth, 'Should parse JSON with whitespace');
// Restore env
if (originalEnv) {
process.env.GOOGLE_SERVICE_ACCOUNT_KEY = originalEnv;
} else {
delete process.env.GOOGLE_SERVICE_ACCOUNT_KEY;
}
});
});
// =============================================================================
// T034: Unit test for credential validation
// =============================================================================
describe('T034: Credential Validation', () => {
it('should detect missing client_email field', async () => {
const invalidCredentials = {
private_key: '-----BEGIN PRIVATE KEY-----\nMOCK_KEY\n-----END PRIVATE KEY-----\n',
project_id: 'test-project'
// Missing client_email
};
process.env.GOOGLE_SERVICE_ACCOUNT_KEY = JSON.stringify(invalidCredentials);
// TODO: Import validateCredentials from src/auth.js
// const { validateCredentials } = await import('../../src/auth.js');
// Should throw error for missing client_email
// await assert.rejects(
// async () => await validateCredentials(invalidCredentials),
// { message: /client_email/ },
// 'Should reject credentials without client_email'
// );
delete process.env.GOOGLE_SERVICE_ACCOUNT_KEY;
});
it('should detect missing private_key field', async () => {
const invalidCredentials = {
client_email: 'test@project.iam.gserviceaccount.com',
project_id: 'test-project'
// Missing private_key
};
process.env.GOOGLE_SERVICE_ACCOUNT_KEY = JSON.stringify(invalidCredentials);
// TODO: Import validateCredentials
// const { validateCredentials } = await import('../../src/auth.js');
// Should throw error for missing private_key
// await assert.rejects(
// async () => await validateCredentials(invalidCredentials),
// { message: /private_key/ },
// 'Should reject credentials without private_key'
// );
delete process.env.GOOGLE_SERVICE_ACCOUNT_KEY;
});
it('should detect missing project_id field', async () => {
const invalidCredentials = {
client_email: 'test@project.iam.gserviceaccount.com',
private_key: '-----BEGIN PRIVATE KEY-----\nMOCK_KEY\n-----END PRIVATE KEY-----\n'
// Missing project_id
};
process.env.GOOGLE_SERVICE_ACCOUNT_KEY = JSON.stringify(invalidCredentials);
// TODO: Import validateCredentials
// const { validateCredentials } = await import('../../src/auth.js');
// Should throw error for missing project_id
// await assert.rejects(
// async () => await validateCredentials(invalidCredentials),
// { message: /project_id/ },
// 'Should reject credentials without project_id'
// );
delete process.env.GOOGLE_SERVICE_ACCOUNT_KEY;
});
it('should detect empty credential fields', async () => {
const invalidCredentials = {
client_email: '', // Empty
private_key: '-----BEGIN PRIVATE KEY-----\nMOCK_KEY\n-----END PRIVATE KEY-----\n',
project_id: 'test-project'
};
process.env.GOOGLE_SERVICE_ACCOUNT_KEY = JSON.stringify(invalidCredentials);
// TODO: Import validateCredentials
// const { validateCredentials } = await import('../../src/auth.js');
// Should throw error for empty client_email
// await assert.rejects(
// async () => await validateCredentials(invalidCredentials),
// { message: /client_email.*empty/ },
// 'Should reject empty client_email'
// );
delete process.env.GOOGLE_SERVICE_ACCOUNT_KEY;
});
it('should accept valid credentials', async () => {
const validCredentials = {
type: 'service_account',
project_id: 'test-project',
private_key: '-----BEGIN PRIVATE KEY-----\nMOCK_KEY\n-----END PRIVATE KEY-----\n',
client_email: 'test@test-project.iam.gserviceaccount.com'
};
process.env.GOOGLE_SERVICE_ACCOUNT_KEY = JSON.stringify(validCredentials);
// TODO: Import validateCredentials
// const { validateCredentials } = await import('../../src/auth.js');
// Should not throw for valid credentials
// await assert.doesNotReject(
// async () => await validateCredentials(validCredentials),
// 'Should accept valid credentials'
// );
delete process.env.GOOGLE_SERVICE_ACCOUNT_KEY;
});
it('should trigger fatal error handler on invalid credentials (exit code 1)', async () => {
// Per T016: Fatal error handler should log to stderr and exit with code 1
const invalidCredentials = {
invalid: 'structure'
};
process.env.GOOGLE_SERVICE_ACCOUNT_KEY = JSON.stringify(invalidCredentials);
// TODO: Import initializeAuth which should call fatal error handler
// const { initializeAuth } = await import('../../src/auth.js');
// Mock process.exit to prevent actual exit
// let exitCode;
// const originalExit = process.exit;
// process.exit = (code) => { exitCode = code; throw new Error('EXIT'); };
// try {
// await initializeAuth();
// } catch (e) {
// if (e.message === 'EXIT') {
// assert.equal(exitCode, 1, 'Should exit with code 1 on invalid credentials');
// } else {
// throw e;
// }
// } finally {
// process.exit = originalExit;
// }
delete process.env.GOOGLE_SERVICE_ACCOUNT_KEY;
});
});