diff --git a/cloud/.gitignore b/cloud/.gitignore index 77895ba63a..1be46eacf3 100644 --- a/cloud/.gitignore +++ b/cloud/.gitignore @@ -9,3 +9,7 @@ coverage dist docker/data +!docker/data/ +docker/data/postgres/ +docker/data/clickhouse/* +!docker/data/clickhouse/.gitkeep diff --git a/cloud/docker/compose.yml b/cloud/docker/compose.yml index 93a0ed4929..92fbdff8ba 100644 --- a/cloud/docker/compose.yml +++ b/cloud/docker/compose.yml @@ -17,5 +17,23 @@ services: timeout: 5s retries: 5 + clickhouse: + image: clickhouse/clickhouse-server:24.10 + container_name: clickhouse-dev + environment: + CLICKHOUSE_DB: mirascope_analytics + CLICKHOUSE_USER: default + CLICKHOUSE_PASSWORD: clickhouse + ports: + - "8123:8123" + - "9000:9000" + volumes: + - ./data/clickhouse:/var/lib/clickhouse + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:8123/ping"] + interval: 5s + timeout: 5s + retries: 5 + volumes: postgres_data: diff --git a/cloud/docker/data/clickhouse/.gitkeep b/cloud/docker/data/clickhouse/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cloud/tests/global-setup.ts b/cloud/tests/global-setup.ts index 1e8b3cdc6f..72417bc54a 100644 --- a/cloud/tests/global-setup.ts +++ b/cloud/tests/global-setup.ts @@ -3,6 +3,11 @@ import { PostgreSqlContainer, type StartedPostgreSqlContainer, } from "@testcontainers/postgresql"; +import { + GenericContainer, + type StartedTestContainer, + Wait, +} from "testcontainers"; import { migrate } from "drizzle-orm/postgres-js/migrator"; import { drizzle } from "drizzle-orm/postgres-js"; import postgres from "postgres"; @@ -10,6 +15,8 @@ import path from "path"; import fs from "fs"; import os from "os"; import { fileURLToPath } from "url"; +import { execFileSync } from "node:child_process"; +import { randomUUID } from "node:crypto"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); @@ -50,11 +57,56 @@ const runMigrations = (connectionUri: string) => // Store container reference for teardown let container: StartedPostgreSqlContainer | null = null; +let clickhouseContainer: StartedTestContainer | null = null; + +const clickhouseImage = "clickhouse/clickhouse-server:24.10"; +const clickhouseUser = process.env.TEST_CLICKHOUSE_USER ?? "default"; +const clickhousePassword = process.env.TEST_CLICKHOUSE_PASSWORD ?? randomUUID(); +const clickhouseDatabase = "mirascope_analytics"; + +const runClickhouseMigrations = (clickhouseUrl: string, nativePort: number) => { + execFileSync("bash", ["clickhouse/migrate.sh", "migrate"], { + cwd: path.resolve(__dirname, ".."), + env: { + ...process.env, + TZ: "UTC", + CLICKHOUSE_URL: clickhouseUrl, + CLICKHOUSE_USER: clickhouseUser, + CLICKHOUSE_PASSWORD: clickhousePassword, + CLICKHOUSE_DATABASE: clickhouseDatabase, + CLICKHOUSE_MIGRATE_NATIVE_PORT: String(nativePort), + }, + stdio: "inherit", + }); +}; // Vitest global setup - runs once before all tests export async function setup() { const scope = Effect.runSync(Scope.make()); + clickhouseContainer = await new GenericContainer(clickhouseImage) + .withExposedPorts(8123, 9000) + .withEnvironment({ + CLICKHOUSE_DB: clickhouseDatabase, + CLICKHOUSE_USER: clickhouseUser, + CLICKHOUSE_PASSWORD: clickhousePassword, + CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: "1", + }) + .withWaitStrategy(Wait.forHttp("/ping", 8123)) + .start(); + + const clickhouseHttpPort = clickhouseContainer.getMappedPort(8123); + const clickhouseNativePort = clickhouseContainer.getMappedPort(9000); + const clickhouseUrl = `http://127.0.0.1:${clickhouseHttpPort}`; + + process.env.CLICKHOUSE_URL = clickhouseUrl; + process.env.CLICKHOUSE_USER = clickhouseUser; + process.env.CLICKHOUSE_PASSWORD = clickhousePassword; + process.env.CLICKHOUSE_DATABASE = clickhouseDatabase; + process.env.CLICKHOUSE_MIGRATE_NATIVE_PORT = String(clickhouseNativePort); + + runClickhouseMigrations(clickhouseUrl, clickhouseNativePort); + container = await Effect.runPromise( Effect.acquireRelease(acquireContainer, (c) => Effect.promise(() => c.stop()), @@ -86,4 +138,8 @@ export async function teardown() { if (container) { await container.stop(); } + + if (clickhouseContainer) { + await clickhouseContainer.stop(); + } } diff --git a/package.json b/package.json index 8241d51d6e..6325e61b02 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "test:python": "cd python && uv run pytest tests/", "test:cloud": "cd cloud && bun run test", "coverage:python": "cd python && uv run pytest --cov --cov-config=.coveragerc --cov-report=term-missing", - "codespell": "uvx codespell", + "codespell": "uvx codespell --skip \"./cloud/docker/data,./cloud/docker/data/**\"", "ci": "bun run lint && bun run coverage:python && bun run docs:build", "lint": "bun run codespell && bun run lint:python && bun run lint:typescript && bun run lint:cloud", "lint:python": "cd python && uv sync --all-extras --dev && uv run ruff check . && uv run pyright .", diff --git a/python/pyproject.toml b/python/pyproject.toml index 3476f1316f..2e7d3b8c60 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -243,7 +243,7 @@ target-python-version = "3.10" type-mappings = ["binary=string"] [tool.codespell] -skip = [".git", "*.lock"] +skip = [".git", "*.lock", "cloud/docker/data"] [tool.coverage.run] omit = []