Ctrl K
ComunidadGuías…

libs/juava/__tests__/objects.test.ts

Rama newjitsu · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { deepMerge } from "../src/objects";

describe("deepMerge", () => {
  describe("keys that reach prototypes", () => {
    afterEach(() => {
      delete (Object.prototype as any).polluted;
    });

    test("never merges into Object.prototype through a __proto__ key from parsed JSON", () => {
      const result = deepMerge({ a: 1 }, JSON.parse('{"__proto__":{"polluted":"yes"},"b":2}'));
      expect(({} as any).polluted).toBeUndefined();
      expect(Object.getPrototypeOf(result)).toBe(Object.prototype);
      expect(result).toEqual({ a: 1, b: 2 });
    });

    test("ignores constructor and prototype keys, and still merges the other keys at every depth", () => {
      const result = deepMerge(
        { a: { keep: 1 } },
        JSON.parse(
          '{"constructor":{"prototype":{"polluted":"yes"}},"prototype":{"polluted":"yes"},"a":{"__proto__":{"polluted":"yes"},"add":2}}'
        )
      );
      expect(({} as any).polluted).toBeUndefined();
      expect(result).toEqual({ a: { keep: 1, add: 2 } });
    });
  });

  describe("primitive source values", () => {
    test("returns source when source is null", () => {
      expect(deepMerge({ a: 1 }, null)).toBe(null);
    });

    test("returns source when source is undefined", () => {
      expect(deepMerge({ a: 1 }, undefined)).toBe(undefined);
    });

    test("returns source when source is a string", () => {
      expect(deepMerge({ a: 1 }, "hello")).toBe("hello");
    });

    test("returns source when source is a number", () => {
      expect(deepMerge({ a: 1 }, 42)).toBe(42);
    });

    test("returns source when source is a boolean", () => {
      expect(deepMerge({ a: 1 }, true)).toBe(true);
      expect(deepMerge({ a: 1 }, false)).toBe(false);
    });
  });

  describe("primitive target values", () => {
    test("returns source when target is null", () => {
      const source = { a: 1 };
      expect(deepMerge(null, source)).toBe(source);
    });

    test("returns source when target is undefined", () => {
      const source = { a: 1 };
      expect(deepMerge(undefined, source)).toBe(source);
    });

    test("returns source when target is a string", () => {
      const source = { a: 1 };
      expect(deepMerge("hello", source)).toBe(source);
    });

    test("returns source when target is a number", () => {
      const source = { a: 1 };
      expect(deepMerge(42, source)).toBe(source);
    });

    test("returns source when target is a boolean", () => {
      const source = { a: 1 };
      expect(deepMerge(true, source)).toBe(source);
      expect(deepMerge(false, source)).toBe(source);
    });
  });

  describe("array handling (arrays are replaced, not merged)", () => {
    test("returns source array when source is an array", () => {
      const source = [1, 2, 3];
      expect(deepMerge({ a: 1 }, source)).toBe(source);
    });

    test("returns source when target is an array", () => {
      const source = { a: 1 };
      expect(deepMerge([1, 2, 3], source)).toBe(source);
    });

    test("replaces target array property with source array", () => {
      const target = { items: [1, 2, 3] };
      const source = { items: [4, 5] };
      const result = deepMerge(target, source);
      expect(result.items).toEqual([4, 5]);
      expect(result.items).toBe(source.items);
    });

    test("replaces nested array in deep object", () => {
      const target = { a: { b: { items: [1, 2, 3] } } };
      const source = { a: { b: { items: [4, 5, 6, 7] } } };
      const result = deepMerge(target, source);
      expect(result.a.b.items).toEqual([4, 5, 6, 7]);
    });

    test("replaces object with array", () => {
      const target = { a: { nested: "value" } };
      const source = { a: [1, 2, 3] };
      const result = deepMerge(target, source);
      expect(result.a).toEqual([1, 2, 3]);
    });

    test("replaces array with object", () => {
      const target = { a: [1, 2, 3] };
      const source = { a: { nested: "value" } };
      const result = deepMerge(target, source);
      expect(result.a).toEqual({ nested: "value" });
    });
  });

  describe("shallow object merging", () => {
    test("merges two flat objects", () => {
      const target = { a: 1, b: 2 };
      const source = { b: 3, c: 4 };
      const result = deepMerge(target, source);
      expect(result).toEqual({ a: 1, b: 3, c: 4 });
    });

    test("source properties override target properties", () => {
      const target = { a: "original" };
      const source = { a: "updated" };
      const result = deepMerge(target, source);
      expect(result.a).toBe("updated");
    });

    test("preserves target properties not in source", () => {
      const target = { a: 1, b: 2 };
      const source = { c: 3 };
      const result = deepMerge(target, source);
      expect(result).toEqual({ a: 1, b: 2, c: 3 });
    });
  });

  describe("deep object merging", () => {
    test("merges nested objects", () => {
      const target = { a: { b: 1, c: 2 } };
      const source = { a: { c: 3, d: 4 } };
      const result = deepMerge(target, source);
      expect(result).toEqual({ a: { b: 1, c: 3, d: 4 } });
    });

    test("merges deeply nested objects", () => {
      const target = { a: { b: { c: { d: 1, e: 2 } } } };
      const source = { a: { b: { c: { e: 3, f: 4 } } } };
      const result = deepMerge(target, source);
      expect(result).toEqual({ a: { b: { c: { d: 1, e: 3, f: 4 } } } });
    });

    test("creates nested structure if target property is undefined", () => {
      const target = { a: 1 };
      const source = { b: { c: { d: 2 } } };
      const result = deepMerge(target, source);
      expect(result).toEqual({ a: 1, b: { c: { d: 2 } } });
    });

    test("replaces primitive with nested object", () => {
      const target = { a: 1 };
      const source = { a: { nested: "value" } };
      const result = deepMerge(target, source);
      expect(result).toEqual({ a: { nested: "value" } });
    });

    test("replaces nested object with primitive", () => {
      const target = { a: { nested: "value" } };
      const source = { a: 1 };
      const result = deepMerge(target, source);
      expect(result).toEqual({ a: 1 });
    });
  });

  describe("null and undefined in nested properties", () => {
    test("source null replaces target value", () => {
      const target = { a: { b: 1 } };
      const source = { a: null };
      const result = deepMerge(target, source);
      expect(result).toEqual({ a: null });
    });

    test("source undefined replaces target value", () => {
      const target = { a: { b: 1 } };
      const source = { a: undefined };
      const result = deepMerge(target, source);
      expect(result).toEqual({ a: undefined });
    });

    test("handles null in nested source object", () => {
      const target = { a: { b: 1, c: 2 } };
      const source = { a: { b: null } };
      const result = deepMerge(target, source);
      expect(result).toEqual({ a: { b: null, c: 2 } });
    });

    test("handles undefined in nested source object", () => {
      const target = { a: { b: 1, c: 2 } };
      const source = { a: { b: undefined } };
      const result = deepMerge(target, source);
      expect(result).toEqual({ a: { b: undefined, c: 2 } });
    });

    test("handles target with null nested property", () => {
      const target = { a: null };
      const source = { a: { b: 1 } };
      const result = deepMerge(target, source);
      expect(result).toEqual({ a: { b: 1 } });
    });

    test("handles target with undefined nested property", () => {
      const target = { a: undefined };
      const source = { a: { b: 1 } };
      const result = deepMerge(target, source);
      expect(result).toEqual({ a: { b: 1 } });
    });
  });

  describe("mutation behavior", () => {
    test("mutates the target object", () => {
      const target = { a: 1 };
      const source = { b: 2 };
      const result = deepMerge(target, source);
      expect(result).toBe(target);
      expect(target).toEqual({ a: 1, b: 2 });
    });

    test("mutates nested target objects", () => {
      const nested = { b: 1 };
      const target = { a: nested };
      const source = { a: { c: 2 } };
      deepMerge(target, source);
      expect(nested).toEqual({ b: 1, c: 2 });
    });

    test("does not mutate source object", () => {
      const target = { a: 1 };
      const source = { b: 2 };
      const sourceCopy = { ...source };
      deepMerge(target, source);
      expect(source).toEqual(sourceCopy);
    });
  });

  describe("empty objects", () => {
    test("merging with empty source returns target unchanged", () => {
      const target = { a: 1, b: 2 };
      const result = deepMerge(target, {});
      expect(result).toEqual({ a: 1, b: 2 });
    });

    test("merging empty target with source returns populated target", () => {
      const target = {};
      const source = { a: 1, b: 2 };
      const result = deepMerge(target, source);
      expect(result).toEqual({ a: 1, b: 2 });
    });

    test("merging two empty objects returns empty object", () => {
      const result = deepMerge({}, {});
      expect(result).toEqual({});
    });

    test("handles empty nested objects", () => {
      const target = { a: {} };
      const source = { a: { b: 1 } };
      const result = deepMerge(target, source);
      expect(result).toEqual({ a: { b: 1 } });
    });
  });

  describe("special object types", () => {
    test("Date objects are replaced, not merged", () => {
      const targetDate = new Date("2020-01-01");
      const sourceDate = new Date("2023-01-01");
      const target = { date: targetDate };
      const source = { date: sourceDate };
      const result = deepMerge(target, source);
      expect(result.date).toBe(sourceDate);
      expect(result.date.getFullYear()).toBe(2023);
    });

    test("returns source Date when source is a Date", () => {
      const date = new Date("2023-01-01");
      expect(deepMerge({ a: 1 }, date)).toBe(date);
    });

    test("returns source when target is a Date", () => {
      const source = { a: 1 };
      expect(deepMerge(new Date("2023-01-01"), source)).toBe(source);
    });

    test("replaces nested object with Date", () => {
      const target = { a: { b: 1 } };
      const date = new Date("2023-01-01");
      const source = { a: date };
      const result = deepMerge(target, source);
      expect(result.a).toBe(date);
    });

    test("replaces nested Date with object", () => {
      const target = { a: new Date("2020-01-01") };
      const source = { a: { b: 1 } };
      const result = deepMerge(target, source);
      expect(result.a).toEqual({ b: 1 });
    });

    test("handles objects with numeric keys", () => {
      const target = { 1: "a", 2: "b" };
      const source = { 2: "c", 3: "d" };
      const result = deepMerge(target, source);
      expect(result).toEqual({ 1: "a", 2: "c", 3: "d" });
    });
  });

  describe("complex real-world scenarios", () => {
    test("merges configuration objects", () => {
      const defaultConfig = {
        server: {
          host: "localhost",
          port: 3000,
          ssl: false,
        },
        database: {
          host: "localhost",
          port: 5432,
          name: "app",
        },
        features: ["feature1", "feature2"],
      };

      const userConfig = {
        server: {
          port: 8080,
          ssl: true,
        },
        features: ["feature3"],
      };

      const result = deepMerge(defaultConfig, userConfig);
      expect(result).toEqual({
        server: {
          host: "localhost",
          port: 8080,
          ssl: true,
        },
        database: {
          host: "localhost",
          port: 5432,
          name: "app",
        },
        features: ["feature3"], // Array replaced, not merged
      });
    });

    test("handles mixed nested structures", () => {
      const target = {
        user: {
          name: "John",
          settings: {
            theme: "dark",
            notifications: {
              email: true,
              sms: false,
            },
          },
          roles: ["user"],
        },
      };

      const source = {
        user: {
          settings: {
            notifications: {
              sms: true,
              push: true,
            },
          },
          roles: ["user", "admin"],
        },
      };

      const result = deepMerge(target, source);
      expect(result).toEqual({
        user: {
          name: "John",
          settings: {
            theme: "dark",
            notifications: {
              email: true,
              sms: true,
              push: true,
            },
          },
          roles: ["user", "admin"], // Array replaced
        },
      });
    });
  });
});