appwrite-utils-cli
Version:
Appwrite Utility Functions to help with database management, data conversion, data import, migrations, and much more. Meant to be used as a CLI tool, I do not recommend installing this in frontend environments.
272 lines (271 loc) • 12.1 kB
JavaScript
import { describe, it, expect, beforeEach, afterEach } from "@jest/globals";
import { ConfigMergeService } from "../ConfigMergeService.js";
describe("ConfigMergeService", () => {
let service;
let baseConfig;
let sessionInfo;
// Store original env vars
const originalEnv = { ...process.env };
beforeEach(() => {
service = new ConfigMergeService();
baseConfig = {
appwriteEndpoint: "https://cloud.appwrite.io/v1",
appwriteProject: "test-project",
appwriteKey: "test-api-key",
databases: [],
buckets: [],
functions: [],
};
sessionInfo = {
projectId: "test-project",
endpoint: "https://cloud.appwrite.io/v1",
cookie: "eyJhbGc.test.cookie",
email: "test@example.com",
};
});
afterEach(() => {
// Restore original env vars
process.env = { ...originalEnv };
});
describe("mergeSession", () => {
it("should merge session cookie into config", () => {
const result = service.mergeSession(baseConfig, sessionInfo);
expect(result.sessionCookie).toBe("eyJhbGc.test.cookie");
expect(result.authMethod).toBe("session");
});
it("should not mutate original config", () => {
const original = { ...baseConfig };
service.mergeSession(baseConfig, sessionInfo);
expect(baseConfig).toEqual(original);
});
it("should preserve all existing config properties", () => {
const result = service.mergeSession(baseConfig, sessionInfo);
expect(result.appwriteEndpoint).toBe(baseConfig.appwriteEndpoint);
expect(result.appwriteProject).toBe(baseConfig.appwriteProject);
expect(result.appwriteKey).toBe(baseConfig.appwriteKey);
});
});
describe("applyOverrides", () => {
it("should apply all provided overrides", () => {
const overrides = {
appwriteEndpoint: "https://custom.appwrite.io/v1",
appwriteProject: "override-project",
appwriteKey: "override-key",
sessionCookie: "override-cookie",
authMethod: "apikey",
};
const result = service.applyOverrides(baseConfig, overrides);
expect(result.appwriteEndpoint).toBe("https://custom.appwrite.io/v1");
expect(result.appwriteProject).toBe("override-project");
expect(result.appwriteKey).toBe("override-key");
expect(result.sessionCookie).toBe("override-cookie");
expect(result.authMethod).toBe("apikey");
});
it("should skip undefined and null values", () => {
const overrides = {
appwriteEndpoint: undefined,
appwriteProject: null,
appwriteKey: "new-key",
};
const result = service.applyOverrides(baseConfig, overrides);
expect(result.appwriteEndpoint).toBe(baseConfig.appwriteEndpoint);
expect(result.appwriteProject).toBe(baseConfig.appwriteProject);
expect(result.appwriteKey).toBe("new-key");
});
it("should not mutate original config", () => {
const original = { ...baseConfig };
const overrides = {
appwriteEndpoint: "https://custom.appwrite.io/v1",
};
service.applyOverrides(baseConfig, overrides);
expect(baseConfig).toEqual(original);
});
});
describe("mergeEnvironmentVariables", () => {
it("should merge environment variables when config values are not set", () => {
process.env.APPWRITE_ENDPOINT = "https://env.appwrite.io/v1";
process.env.APPWRITE_PROJECT = "env-project";
process.env.APPWRITE_API_KEY = "env-api-key";
process.env.APPWRITE_SESSION_COOKIE = "env-cookie";
const emptyConfig = {
databases: [],
buckets: [],
functions: [],
};
const result = service.mergeEnvironmentVariables(emptyConfig);
expect(result.appwriteEndpoint).toBe("https://env.appwrite.io/v1");
expect(result.appwriteProject).toBe("env-project");
expect(result.appwriteKey).toBe("env-api-key");
expect(result.sessionCookie).toBe("env-cookie");
});
it("should not override existing config values", () => {
process.env.APPWRITE_ENDPOINT = "https://env.appwrite.io/v1";
process.env.APPWRITE_PROJECT = "env-project";
const result = service.mergeEnvironmentVariables(baseConfig);
expect(result.appwriteEndpoint).toBe(baseConfig.appwriteEndpoint);
expect(result.appwriteProject).toBe(baseConfig.appwriteProject);
});
it("should ignore empty environment variables", () => {
process.env.APPWRITE_ENDPOINT = "";
process.env.APPWRITE_PROJECT = "";
const emptyConfig = {
databases: [],
buckets: [],
functions: [],
};
const result = service.mergeEnvironmentVariables(emptyConfig);
expect(result.appwriteEndpoint).toBeUndefined();
expect(result.appwriteProject).toBeUndefined();
});
});
describe("mergeSources", () => {
it("should merge multiple sources in priority order", () => {
const source1 = {
appwriteEndpoint: "https://source1.appwrite.io/v1",
appwriteProject: "source1-project",
};
const source2 = {
appwriteProject: "source2-project",
appwriteKey: "source2-key",
};
const source3 = {
appwriteKey: "source3-key",
sessionCookie: "source3-cookie",
};
const result = service.mergeSources([source1, source2, source3]);
expect(result.appwriteEndpoint).toBe("https://source1.appwrite.io/v1");
expect(result.appwriteProject).toBe("source2-project");
expect(result.appwriteKey).toBe("source3-key");
expect(result.sessionCookie).toBe("source3-cookie");
});
it("should handle arrays by replacing, not merging", () => {
const source1 = {
appwriteEndpoint: "https://cloud.appwrite.io/v1",
appwriteProject: "test",
databases: [{ $id: "db1", name: "db1" }],
};
const source2 = {
databases: [{ $id: "db2", name: "db2" }, { $id: "db3", name: "db3" }],
};
const result = service.mergeSources([source1, source2]);
expect(result.databases).toHaveLength(2);
expect(result.databases).toEqual([{ $id: "db2", name: "db2" }, { $id: "db3", name: "db3" }]);
});
it("should skip undefined and null sources", () => {
const source1 = {
appwriteEndpoint: "https://cloud.appwrite.io/v1",
appwriteProject: "test",
};
const result = service.mergeSources([
source1,
undefined,
null,
{ appwriteKey: "key" },
]);
expect(result.appwriteEndpoint).toBe("https://cloud.appwrite.io/v1");
expect(result.appwriteProject).toBe("test");
expect(result.appwriteKey).toBe("key");
});
it("should throw error when no valid sources provided", () => {
expect(() => service.mergeSources([])).toThrow("No valid configuration sources provided for merging");
expect(() => service.mergeSources([undefined, null])).toThrow("No valid configuration sources provided for merging");
});
it("should throw error when merged config lacks required fields", () => {
const invalidSource = {
appwriteKey: "key",
};
expect(() => service.mergeSources([invalidSource])).toThrow("Merged configuration is missing required fields");
});
});
describe("mergeAllSources", () => {
it("should merge all sources in correct priority order", () => {
process.env.APPWRITE_ENDPOINT = "https://env.appwrite.io/v1";
process.env.APPWRITE_API_KEY = "env-key";
const overrides = {
appwriteKey: "override-key",
};
const result = service.mergeAllSources(baseConfig, {
session: sessionInfo,
overrides,
includeEnv: true,
});
// Base config endpoint
expect(result.appwriteEndpoint).toBe(baseConfig.appwriteEndpoint);
// Session should set session cookie
expect(result.sessionCookie).toBe(sessionInfo.cookie);
// Override should win for API key
expect(result.appwriteKey).toBe("override-key");
});
it("should exclude environment variables when includeEnv is false", () => {
process.env.APPWRITE_SESSION_COOKIE = "env-cookie";
const configWithoutKey = {
...baseConfig,
appwriteKey: undefined,
};
const result = service.mergeAllSources(configWithoutKey, {
includeEnv: false,
});
expect(result.sessionCookie).toBeUndefined();
});
it("should work with only base config", () => {
const result = service.mergeAllSources(baseConfig, {});
expect(result.appwriteEndpoint).toBe(baseConfig.appwriteEndpoint);
expect(result.appwriteProject).toBe(baseConfig.appwriteProject);
});
it("should apply priority: overrides > session > env > base", () => {
process.env.APPWRITE_KEY = "env-key";
const configWithoutKey = {
...baseConfig,
appwriteKey: undefined,
sessionCookie: undefined,
};
// Test with session (should override env)
const withSession = service.mergeAllSources(configWithoutKey, {
session: sessionInfo,
includeEnv: true,
});
expect(withSession.sessionCookie).toBe(sessionInfo.cookie);
expect(withSession.authMethod).toBe("session");
// Test with override (should override session)
const withOverride = service.mergeAllSources(configWithoutKey, {
session: sessionInfo,
overrides: { authMethod: "apikey" },
includeEnv: true,
});
expect(withOverride.authMethod).toBe("apikey");
});
});
describe("deep merge behavior", () => {
it("should deeply merge nested objects", () => {
const source1 = {
appwriteEndpoint: "https://cloud.appwrite.io/v1",
appwriteProject: "test",
databases: [
{
$id: "db1",
name: "Database 1",
},
],
};
const source2 = {
databases: [
{
$id: "db1",
name: "Updated Database",
},
],
};
const result = service.mergeSources([source1, source2]);
// Arrays are replaced, not merged
expect(result.databases).toHaveLength(1);
expect(result.databases[0].name).toBe("Updated Database");
});
it("should preserve immutability throughout merge chain", () => {
const original1 = { ...baseConfig };
const original2 = { appwriteKey: "new-key" };
service.mergeSources([baseConfig, original2]);
expect(baseConfig).toEqual(original1);
expect(original2).toEqual({ appwriteKey: "new-key" });
});
});
});