Skip to main content
Glama

Bucket Feature Flags MCP Server

Official
by reflagcom
client.test.ts7.53 kB
import { beforeEach, describe, expect, it, vi } from "vitest"; import { ReflagClient } from "../src/client"; import { FlagsClient } from "../src/flag/flags"; import { HttpClient } from "../src/httpClient"; import { flagsResult } from "./mocks/handlers"; describe("ReflagClient", () => { let client: ReflagClient; const httpClientPost = vi.spyOn(HttpClient.prototype as any, "post"); const httpClientGet = vi.spyOn(HttpClient.prototype as any, "get"); const flagClientSetContext = vi.spyOn(FlagsClient.prototype, "setContext"); beforeEach(() => { client = new ReflagClient({ publishableKey: "test-key", user: { id: "user1" }, company: { id: "company1" }, }); vi.clearAllMocks(); }); describe("updateUser", () => { it("should update the user context", async () => { // and send new user data and trigger flag update const updatedUser = { name: "New User" }; await client.updateUser(updatedUser); expect(client["context"].user).toEqual({ id: "user1", ...updatedUser }); expect(httpClientPost).toHaveBeenCalledWith({ path: "/user", body: { userId: "user1", attributes: { name: updatedUser.name }, }, }); expect(flagClientSetContext).toHaveBeenCalledWith(client["context"]); }); }); describe("updateCompany", () => { it("should update the company context", async () => { // send new company data and trigger flag update const updatedCompany = { name: "New Company" }; await client.updateCompany(updatedCompany); expect(client["context"].company).toEqual({ id: "company1", ...updatedCompany, }); expect(httpClientPost).toHaveBeenCalledWith({ path: "/company", body: { userId: "user1", companyId: "company1", attributes: { name: updatedCompany.name }, }, }); expect(flagClientSetContext).toHaveBeenCalledWith(client["context"]); }); }); describe("getFlag", () => { it("takes overrides into account", async () => { await client.initialize(); expect(flagsResult["flagA"].isEnabled).toBe(true); expect(client.getFlag("flagA").isEnabled).toBe(true); client.getFlag("flagA").setIsEnabledOverride(false); expect(client.getFlag("flagA").isEnabled).toBe(false); }); }); describe("hooks integration", () => { it("on adds hooks appropriately, off removes them", async () => { const trackHook = vi.fn(); const userHook = vi.fn(); const companyHook = vi.fn(); const checkHook = vi.fn(); const flagsUpdated = vi.fn(); client.on("track", trackHook); client.on("user", userHook); client.on("company", companyHook); client.on("check", checkHook); client.on("flagsUpdated", flagsUpdated); await client.track("test-event"); expect(trackHook).toHaveBeenCalledWith({ eventName: "test-event", attributes: undefined, user: client["context"].user, company: client["context"].company, }); await client["user"](); expect(userHook).toHaveBeenCalledWith(client["context"].user); await client["company"](); expect(companyHook).toHaveBeenCalledWith(client["context"].company); // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- special getter triggering event client.getFlag("flagA").isEnabled; expect(checkHook).toHaveBeenCalled(); checkHook.mockReset(); // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- special getter triggering event client.getFlag("flagA").config; expect(checkHook).toHaveBeenCalled(); expect(flagsUpdated).not.toHaveBeenCalled(); await client.updateOtherContext({ key: "value" }); expect(flagsUpdated).toHaveBeenCalled(); // Remove hooks client.off("track", trackHook); client.off("user", userHook); client.off("company", companyHook); client.off("check", checkHook); client.off("flagsUpdated", flagsUpdated); // Reset mocks trackHook.mockReset(); userHook.mockReset(); companyHook.mockReset(); checkHook.mockReset(); flagsUpdated.mockReset(); // Trigger events again await client.track("test-event"); await client["user"](); await client["company"](); // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- special getter triggering event client.getFlag("flagA").isEnabled; // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- special getter triggering event client.getFlag("flagA").config; await client.updateOtherContext({ key: "value" }); // Ensure hooks are not called expect(trackHook).not.toHaveBeenCalled(); expect(userHook).not.toHaveBeenCalled(); expect(companyHook).not.toHaveBeenCalled(); expect(checkHook).not.toHaveBeenCalled(); expect(flagsUpdated).not.toHaveBeenCalled(); }); }); describe("offline mode", () => { it("should not make HTTP calls when offline", async () => { client = new ReflagClient({ publishableKey: "test-key", user: { id: "user1" }, company: { id: "company1" }, offline: true, feedback: { enableAutoFeedback: true }, }); await client.initialize(); await client.track("offline-event"); await client.feedback({ flagKey: "flagA", score: 5 }); await client.updateUser({ name: "New User" }); await client.updateCompany({ name: "New Company" }); await client.stop(); expect(httpClientPost).not.toHaveBeenCalled(); expect(httpClientGet).not.toHaveBeenCalled(); }); }); describe("bootstrap parameter", () => { const flagsClientInitialize = vi.spyOn(FlagsClient.prototype, "initialize"); beforeEach(() => { flagsClientInitialize.mockClear(); }); it("should use pre-fetched flags and skip initialization when flags are provided", async () => { const preFetchedFlags = { testFlag: { key: "testFlag", isEnabled: true, targetingVersion: 1, }, }; // Create a spy to monitor maybeFetchFlags which should not be called if already initialized const maybeFetchFlags = vi.spyOn( FlagsClient.prototype as any, "maybeFetchFlags", ); client = new ReflagClient({ publishableKey: "test-key", user: { id: "user1" }, company: { id: "company1" }, bootstrappedFlags: preFetchedFlags, feedback: { enableAutoFeedback: false }, // Disable to avoid HTTP calls }); // FlagsClient should be bootstrapped but not initialized in constructor when flags are provided expect(client["flagsClient"]["bootstrapped"]).toBe(true); expect(client["flagsClient"]["initialized"]).toBe(false); expect(client.getFlags()).toEqual({ testFlag: { key: "testFlag", isEnabled: true, targetingVersion: 1, isEnabledOverride: null, }, }); maybeFetchFlags.mockClear(); await client.initialize(); // After initialize, flagsClient should be properly initialized expect(client["flagsClient"]["initialized"]).toBe(true); // maybeFetchFlags should not be called since flagsClient is already bootstrapped expect(maybeFetchFlags).not.toHaveBeenCalled(); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/reflagcom/bucket-javascript-sdk'

If you have feedback or need assistance with the MCP directory API, please join our Discord server