# test.govlab/content-fingerprint/core/converters/fingerprint.converter.test.ts

> 60 lines of code and 0 definitions.

Tree: Codebase Testing
Language: typescript
Canonical: https://banes-lab.com/anatomy/testing#file-testing-test-govlab-content-fingerprint-core-converters-fingerprint-converter-test-ts
Source text: https://banes-lab.com/source/testing/test.govlab/content-fingerprint/core/converters/fingerprint.converter.test.ts.txt

Listed in [test.govlab/content-fingerprint/core/converters](https://banes-lab.com/api/source/testing/test.govlab/content-fingerprint/core/converters.md), before [test.govlab/content-fingerprint/core/converters/fingerprint.fixture.ts](https://banes-lab.com/source/testing/test.govlab/content-fingerprint/core/converters/fingerprint.fixture.ts.md).

## Contained in

- [test.govlab/content-fingerprint/core/converters](https://banes-lab.com/anatomy/testing/folder-testing-test-govlab-content-fingerprint-core-converters.md)

## Source

```typescript
import { ABSENT_SENTINEL, digestOf, fingerprint, fingerprintOf, hashFile } from "@govlab/content-fingerprint";
import {
    HASH_ALGORITHM,
    HASH_ENCODING,
    RECORD_SEPARATOR,
    UNIT_SEPARATOR,
} from "@govlab/content-fingerprint/configuration/constants/fingerprint.constants.ts";
import { describe, expect, it } from "vitest";
import { createHash } from "node:crypto";
import { join } from "node:path";
import { withTemp } from "./fingerprint.fixture.ts";
import { writeVerbatim } from "@govlab/canonical-write";

describe("hashFile", () => {
    it("hashes file bytes deterministically and returns a sentinel for a missing file", () => {
        withTemp((dir) => {
            const file = join(dir, "a.txt");
            writeVerbatim(file, "one");
            expect(hashFile(file)).toBe(createHash(HASH_ALGORITHM).update("one").digest(HASH_ENCODING));
            expect(hashFile(join(dir, "missing.txt"))).toBe(ABSENT_SENTINEL);
        });
    });
});

describe("fingerprint", () => {
    it("is independent of input order but sensitive to content", () => {
        withTemp((dir) => {
            const a = join(dir, "a.txt");
            const b = join(dir, "b.txt");
            writeVerbatim(a, "alpha");
            writeVerbatim(b, "beta");
            const forward = fingerprint([a, b]);
            expect(fingerprint([b, a])).toBe(forward);
            writeVerbatim(b, "beta-changed");
            expect(fingerprint([a, b])).not.toBe(forward);
        });
    });

    it("folds each path and its content hash between the unit and record separators", () => {
        withTemp((dir) => {
            const a = join(dir, "a.txt");
            writeVerbatim(a, "alpha");
            const expected = createHash(HASH_ALGORITHM)
                .update(`${a}${UNIT_SEPARATOR}${hashFile(a)}${RECORD_SEPARATOR}`)
                .digest(HASH_ENCODING);
            expect(fingerprint([a])).toBe(expected);
        });
    });
});

describe("digestOf", () => {
    it("hashes a body's exact bytes, so it differs from the separated composite of the same body", () => {
        const body = '{"a":1}\n';
        expect(digestOf(body)).toBe(createHash(HASH_ALGORITHM).update(body).digest(HASH_ENCODING));
        expect(digestOf(body)).not.toBe(fingerprintOf([body]));
    });
});

describe("fingerprintOf", () => {
    it("combines ordered parts deterministically", () => {
        const combined = fingerprintOf(["shared", "own"]);
        expect(fingerprintOf(["shared", "own"])).toBe(combined);
        expect(fingerprintOf(["own", "shared"])).not.toBe(combined);
    });
});
```
