1import { DEFAULT_SERVER_SETTINGS } from "@t3tools/contracts";
2import { act, type ReactNode } from "react";
3import { create, type ReactTestRenderer } from "react-test-renderer";
4import { afterEach, describe, expect, it, vi } from "vite-plus/test";
5
6const { state, updateSettings } = vi.hoisted(() => ({
7 state: { observability: {} as typeof DEFAULT_SERVER_SETTINGS.observability },
8 updateSettings: vi.fn(async () => ({ _tag: "Success" as const, value: undefined })),
9}));
10
11vi.mock("@effect/atom-react", () => ({ useAtomValue: () => true }));
12vi.mock("../../state/server", () => ({
13 serverEnvironment: {
14 updateSettings: "updateSettings",
15 checkOtlpEndpoint: { permissionAtom: () => null },
16 },
17}));
18vi.mock("../../state/use-atom-command", () => ({
19 useAtomCommand: (command: unknown) => (command === "updateSettings" ? updateSettings : vi.fn()),
20}));
21vi.mock("./SettingsScopeContext", () => ({
22 useSettingsScope: () => ({
23 scope: { kind: "environment" },
24 environment: {
25 environmentId: "env",
26 label: "MAC1",
27 serverConfig: { environment: { capabilities: {} } },
28 },
29 }),
30}));
31vi.mock("./useScopedSettings", () => ({
32 useScopedSettings: (select: (settings: typeof state) => unknown) => select(state),
33}));
34vi.mock("./settingsLayout", () => ({
35 SettingsSection: ({ children }: { children: ReactNode }) => <section>{children}</section>,
36}));
37
38import { TelemetryExportSettings } from "./TelemetryExportSettings";
39
40let renderer: ReactTestRenderer | null = null;
41afterEach(async () => {
42 await act(() => renderer?.unmount());
43 renderer = null;
44 updateSettings.mockClear();
45});
46
47const type = (id: string, value: string) =>
48 act(() => renderer!.root.findByProps({ id }).props.onChange({ target: { value } }));
49
50describe("TelemetryExportSettings", () => {
51 it("does not resend a field the user restored after another client changed it", async () => {
52 state.observability = {
53 ...DEFAULT_SERVER_SETTINGS.observability,
54 otlpTracesUrl: "http://old/v1/traces",
55 };
56 await act(() => {
57 renderer = create(<TelemetryExportSettings />);
58 });
59
60 await type("otlpTracesUrl", "http://draft/v1/traces");
61 await type("otlpTracesUrl", "http://old/v1/traces");
62 // Another client saves a new traces endpoint.
63 state.observability = { ...state.observability, otlpTracesUrl: "http://other/v1/traces" };
64 await act(() => renderer!.update(<TelemetryExportSettings />));
65 await type("otlpMetricsUrl", "http://new/v1/metrics");
66 await act(() => renderer!.root.findByType("form").props.onSubmit({ preventDefault() {} }));
67
68 expect(updateSettings).toHaveBeenCalledWith({
69 environmentId: "env",
70 input: { patch: { observability: { otlpMetricsUrl: "http://new/v1/metrics" } } },
71 });
72 });
73});