Skip to content

Clean up DataLogger and use auth by default #5623

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

192 changes: 192 additions & 0 deletions core/data/log.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import { DevDataLogEvent } from "@continuedev/config-yaml";
import fs from "fs";
import path from "path";
import { IdeInfo, IdeSettings } from "..";
import { Core } from "../core";
import { getDevDataFilePath } from "../util/paths";
import { DataLogger } from "./log";

// Only mock fetch, not fs
jest.mock("@continuedev/fetch");

const TEST_EVENT: DevDataLogEvent = {
name: "tokensGenerated",
data: {
generatedTokens: 100,
model: "gpt-4",
promptTokens: 50,
provider: "openai",
},
};
const SCHEMA = "0.2.0";

describe("DataLogger", () => {
let dataLogger: DataLogger;
const tempDir = path.join(process.cwd(), "temp-test-data");
const testFilePath = path.join(tempDir, "tokensGenerated-test.jsonl");

// Create temp directory for test files
beforeAll(() => {
if (!fs.existsSync(tempDir)) {
fs.mkdirSync(tempDir, { recursive: true });
}
});

// Clean up temp directory after tests
afterAll(() => {
if (fs.existsSync(tempDir)) {
fs.rmSync(tempDir, { recursive: true, force: true });
}
});

beforeEach(() => {
// Reset mocks
jest.clearAllMocks();

// Remove test file if it exists
if (fs.existsSync(testFilePath)) {
fs.unlinkSync(testFilePath);
}

// Get singleton instance
dataLogger = DataLogger.getInstance();

// Mock core and required promises
dataLogger.core = {
configHandler: {
loadConfig: jest.fn().mockResolvedValue({
config: {
data: [],
},
}),
currentProfile: {
profileDescription: {
id: "test-profile-id",
},
},
controlPlaneClient: {
getAccessToken: jest.fn().mockResolvedValue("test-access-token"),
},
},
} as unknown as Core;

dataLogger.ideSettingsPromise = Promise.resolve({
userToken: "test-user-token",
} as IdeSettings);

dataLogger.ideInfoPromise = Promise.resolve({
name: "VSCode",
version: "1.0.0",
extensionVersion: "0.1.0",
} as IdeInfo);
});

describe("getInstance", () => {
it("should return the same instance when called multiple times", () => {
const instance1 = DataLogger.getInstance();
const instance2 = DataLogger.getInstance();
expect(instance1).toBe(instance2);
});
});

describe("addBaseValues", () => {
it("should add base values to event data based on schema", async () => {
const mockZodSchema = {
shape: {
eventName: true,
timestamp: true,
schema: true,
userAgent: true,
selectedProfileId: true,
userId: true,
},
};

const result = await dataLogger.addBaseValues(
{ customField: "value" },
"testEvent",
SCHEMA,
mockZodSchema as any,
);

expect(result).toEqual({
customField: "value",
eventName: "testEvent",
timestamp: expect.any(String),
schema: SCHEMA,
userAgent: "VSCode/1.0.0 (Continue/0.1.0)",
selectedProfileId: "test-profile-id",
userId: "test-user-token",
});
});

it("should not add fields not present in schema shape", async () => {
const mockZodSchema = {
shape: {
eventName: true,
// No other fields
},
};

const result = await dataLogger.addBaseValues(
{ customField: "value" },
"testEvent",
SCHEMA,
mockZodSchema as any,
);

expect(result).toEqual({
customField: "value",
eventName: "testEvent",
});
});
});

describe("logLocalData", () => {
it("should actually write data to the local file", async () => {
// Call the method to log data locally
await dataLogger.logLocalData(TEST_EVENT);

// Verify the file was created
const filepath = getDevDataFilePath(TEST_EVENT.name, SCHEMA);
expect(fs.existsSync(filepath)).toBe(true);

// Read file contents and verify
const fileContent = fs.readFileSync(filepath, "utf8");
expect(fileContent).toContain('"generatedTokens":100');
expect(fileContent).toContain('"model":"gpt-4"');
expect(fileContent).toContain('"eventName":"tokensGenerated"');
});
});

describe("logDevData", () => {
it("should log data locally and to configured destinations", async () => {
// Spy on logLocalData and logToOneDestination
const logLocalDataSpy = jest
.spyOn(dataLogger, "logLocalData")
.mockResolvedValue();
const logToOneDestinationSpy = jest
.spyOn(dataLogger, "logToOneDestination")
.mockResolvedValue();

// Mock config with multiple data destinations
const mockConfig = {
config: {
data: [
{ destination: "https://example.com/logs", schema: SCHEMA },
{ destination: "file:///logs", schema: SCHEMA },
],
},
};

dataLogger.core!.configHandler.loadConfig = jest
.fn()
.mockResolvedValue(mockConfig);

await dataLogger.logDevData(TEST_EVENT);

expect(logLocalDataSpy).toHaveBeenCalledWith(TEST_EVENT);
expect(logToOneDestinationSpy).toHaveBeenCalledTimes(2);
});
});
});
Loading
Loading