feat(config): Restructure .taskmasterconfig and enhance gateway integration

Config Structure Changes and Gateway Integration

## Configuration Structure Changes
- Restructured .taskmasterconfig to use 'account' section for user settings
- Moved userId, userEmail, mode, telemetryEnabled from global to account section
- API keys remain isolated in .env file (not accessible to AI)
- Enhanced getUserId() to always return value, never null (sets default '1234567890')

## Gateway Integration Enhancements
- Updated registerUserWithGateway() to accept both email and userId parameters
- Enhanced /auth/init endpoint integration for existing user validation
- API key updates automatically written to .env during registration process
- Improved user identification and validation flow

## Code Updates for New Structure
- Fixed config-manager.js getter functions for account section access
- Updated user-management.js to use config.account.userId/mode
- Modified telemetry-submission.js to read from account section
- Added getTelemetryEnabled() function with proper account section access
- Enhanced telemetry configuration reading with new structure

## Comprehensive Test Updates
- Updated integration tests (init-config.test.js) for new config structure
- Fixed unit tests (config-manager.test.js) with updated default config
- Updated telemetry tests (telemetry-submission.test.js) for account structure
- Added missing getTelemetryEnabled mock to ai-services-unified.test.js
- Fixed all test expectations to use config.account.* instead of config.global.*
- Removed references to deprecated config.subscription object

## Configuration Access Consistency
- Standardized configuration access patterns across entire codebase
- Clean separation: user settings in account, API keys in .env, models/global in respective sections
- All tests passing with new configuration structure
- Maintained backward compatibility during transition

Changes support enhanced telemetry system with proper user management and gateway integration while maintaining security through API key isolation.
This commit is contained in:
Eyal Toledano
2025-05-30 18:53:16 -04:00
parent e573db3b3b
commit 4e9d58a1b0
18 changed files with 1900 additions and 1609 deletions

View File

@@ -231,4 +231,105 @@ describe("Telemetry Enhancements - Task 90", () => {
expect(result.userId).toBe("test-user-123");
});
});
describe("Subtask 90.4: Non-AI command telemetry queue", () => {
let mockTelemetryQueue;
beforeEach(() => {
// Mock the telemetry queue module
mockTelemetryQueue = {
addToQueue: jest.fn(),
processQueue: jest.fn(),
startBackgroundProcessor: jest.fn(),
stopBackgroundProcessor: jest.fn(),
getQueueStats: jest.fn(() => ({ pending: 0, processed: 0, failed: 0 })),
};
});
it("should add non-AI command telemetry to queue without blocking", async () => {
const commandData = {
timestamp: new Date().toISOString(),
userId: "test-user-123",
commandName: "list-tasks",
executionTimeMs: 45,
success: true,
arguments: { status: "pending" },
};
// Should return immediately without waiting
const startTime = Date.now();
mockTelemetryQueue.addToQueue(commandData);
const endTime = Date.now();
expect(endTime - startTime).toBeLessThan(10); // Should be nearly instantaneous
expect(mockTelemetryQueue.addToQueue).toHaveBeenCalledWith(commandData);
});
it("should process queued telemetry in background", async () => {
const queuedItems = [
{
commandName: "set-status",
executionTimeMs: 23,
success: true,
},
{
commandName: "next-task",
executionTimeMs: 12,
success: true,
},
];
mockTelemetryQueue.processQueue.mockResolvedValue({
processed: 2,
failed: 0,
errors: [],
});
const result = await mockTelemetryQueue.processQueue();
expect(result.processed).toBe(2);
expect(result.failed).toBe(0);
expect(mockTelemetryQueue.processQueue).toHaveBeenCalled();
});
it("should handle queue processing failures gracefully", async () => {
mockTelemetryQueue.processQueue.mockResolvedValue({
processed: 1,
failed: 1,
errors: ["Network timeout for item 2"],
});
const result = await mockTelemetryQueue.processQueue();
expect(result.processed).toBe(1);
expect(result.failed).toBe(1);
expect(result.errors).toContain("Network timeout for item 2");
});
it("should provide queue statistics", () => {
mockTelemetryQueue.getQueueStats.mockReturnValue({
pending: 5,
processed: 127,
failed: 3,
lastProcessedAt: new Date().toISOString(),
});
const stats = mockTelemetryQueue.getQueueStats();
expect(stats.pending).toBe(5);
expect(stats.processed).toBe(127);
expect(stats.failed).toBe(3);
expect(stats.lastProcessedAt).toBeDefined();
});
it("should start and stop background processor", () => {
mockTelemetryQueue.startBackgroundProcessor(30000); // 30 second interval
expect(mockTelemetryQueue.startBackgroundProcessor).toHaveBeenCalledWith(
30000
);
mockTelemetryQueue.stopBackgroundProcessor();
expect(mockTelemetryQueue.stopBackgroundProcessor).toHaveBeenCalled();
});
});
});

View File

@@ -34,6 +34,7 @@ jest.unstable_mockModule(
getProjectName: jest.fn(() => "Test Project"),
getDefaultPriority: jest.fn(() => "medium"),
getDefaultNumTasks: jest.fn(() => 10),
getTelemetryEnabled: jest.fn(() => true),
})
);
@@ -48,17 +49,17 @@ const { getConfig } = await import(
"../../../../scripts/modules/config-manager.js"
);
describe("Telemetry Submission Service - Task 90.2", () => {
describe("Telemetry Submission Service", () => {
beforeEach(() => {
jest.clearAllMocks();
global.fetch.mockClear();
});
describe("Subtask 90.2: Send telemetry data to remote database endpoint", () => {
describe("should send telemetry data to remote database endpoint", () => {
it("should successfully submit telemetry data to hardcoded gateway endpoint", async () => {
// Mock successful config with proper structure
getConfig.mockReturnValue({
global: {
account: {
userId: "test-user-id",
},
});
@@ -113,7 +114,7 @@ describe("Telemetry Submission Service - Task 90.2", () => {
it("should implement retry logic for failed requests", async () => {
getConfig.mockReturnValue({
global: {
account: {
userId: "test-user-id",
},
});
@@ -149,7 +150,7 @@ describe("Telemetry Submission Service - Task 90.2", () => {
it("should handle failures gracefully without blocking execution", async () => {
getConfig.mockReturnValue({
global: {
account: {
userId: "test-user-id",
},
});
@@ -180,8 +181,16 @@ describe("Telemetry Submission Service - Task 90.2", () => {
}, 10000);
it("should respect user opt-out preferences", async () => {
// Mock getTelemetryEnabled to return false for this test
const { getTelemetryEnabled } = await import(
"../../../../scripts/modules/config-manager.js"
);
getTelemetryEnabled.mockReturnValue(false);
getConfig.mockReturnValue({
telemetryEnabled: false,
account: {
telemetryEnabled: false,
},
});
const telemetryData = {
@@ -198,11 +207,14 @@ describe("Telemetry Submission Service - Task 90.2", () => {
expect(result.skipped).toBe(true);
expect(result.reason).toBe("Telemetry disabled by user preference");
expect(global.fetch).not.toHaveBeenCalled();
// Reset the mock for other tests
getTelemetryEnabled.mockReturnValue(true);
});
it("should validate telemetry data before submission", async () => {
getConfig.mockReturnValue({
global: {
account: {
userId: "test-user-id",
},
});
@@ -229,7 +241,7 @@ describe("Telemetry Submission Service - Task 90.2", () => {
it("should handle HTTP error responses appropriately", async () => {
getConfig.mockReturnValue({
global: {
account: {
userId: "test-user-id",
},
});