File size: 4,939 Bytes
6c3af4e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
af13576
6c3af4e
af13576
 
 
 
6c3af4e
af13576
 
6c3af4e
 
af13576
6c3af4e
 
 
af13576
6c3af4e
af13576
 
 
6c3af4e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import { describe, expect, it, vi } from "vitest";
import { getPageReadStats, recordPageRead } from "./pageReadsSinceLastRestart";

/**
 * The counters live for the process, so every case measures a delta rather
 * than an absolute, which is also how they are read in production.
 */
function delta(run: () => void) {
  const before = getPageReadStats();
  run();
  const after = getPageReadStats();

  return {
    requested: after.requested - before.requested,
    read: after.read - before.read,
    bodiesTruncated: after.bodiesTruncated - before.bodiesTruncated,
    skipped: Object.fromEntries(
      Object.entries(after.skipped).map(([outcome, count]) => [
        outcome,
        count - before.skipped[outcome as keyof typeof before.skipped],
      ]),
    ),
    after,
  };
}

describe("recordPageRead", () => {
  it("counts a successful read", () => {
    const counted = delta(() =>
      recordPageRead({ outcome: "read", durationMs: 100 }),
    );

    expect(counted.requested).toBe(1);
    expect(counted.read).toBe(1);
  });

  it("counts every outcome against the total requested", () => {
    const counted = delta(() => {
      recordPageRead({ outcome: "blocked", durationMs: 1 });
      recordPageRead({ outcome: "skippedByBreaker", durationMs: 0 });
      recordPageRead({ outcome: "notADocument", durationMs: 2 });
      recordPageRead({ outcome: "httpForbidden", durationMs: 3 });
      recordPageRead({ outcome: "httpNotFound", durationMs: 4 });
      recordPageRead({ outcome: "httpOtherError", durationMs: 5 });
      recordPageRead({ outcome: "redirectLimit", durationMs: 6 });
      recordPageRead({ outcome: "timedOut", durationMs: 6000 });
      recordPageRead({ outcome: "tooLittleText", durationMs: 7 });
      recordPageRead({ outcome: "failed", durationMs: 8 });
    });

    expect(counted.requested).toBe(10);
    expect(counted.read).toBe(0);
    expect(counted.skipped).toEqual({
      blocked: 1,
      skippedByBreaker: 1,
      notADocument: 1,
      httpForbidden: 1,
      httpNotFound: 1,
      httpOtherError: 1,
      redirectLimit: 1,
      timedOut: 1,
      tooLittleText: 1,
      failed: 1,
    });
  });

  it("keeps read plus the skipped reasons equal to requested", () => {
    const { after } = delta(() =>
      recordPageRead({ outcome: "timedOut", durationMs: 6000 }),
    );

    const accounted =
      after.read +
      Object.values(after.skipped).reduce((total, count) => total + count, 0);

    expect(accounted).toBe(after.requested);
  });

  it("counts a truncated body only when it was truncated", () => {
    const counted = delta(() => {
      recordPageRead({ outcome: "read", durationMs: 10, bodyTruncated: true });
      recordPageRead({ outcome: "read", durationMs: 10 });
    });

    expect(counted.read).toBe(2);
    expect(counted.bodiesTruncated).toBe(1);
  });
});

describe("getPageReadStats", () => {
  it("reports a rate and an average over everything requested", () => {
    const before = getPageReadStats();
    recordPageRead({ outcome: "read", durationMs: 300 });
    recordPageRead({ outcome: "timedOut", durationMs: 6000 });
    const after = getPageReadStats();

    expect(after.requested).toBe(before.requested + 2);
    expect(after.readRate).toBeGreaterThan(0);
    expect(after.readRate).toBeLessThanOrEqual(100);
    // A timeout is part of the cost of the feature, so it belongs in the mean.
    expect(after.averageReadMs).toBeGreaterThan(0);
  });

  it("reports zeroes rather than NaN before anything has been read", async () => {
    // A fresh module instance is the only way to see the pre-first-read state,
    // where every derived number divides by zero.
    vi.resetModules();
    const fresh = await import("./pageReadsSinceLastRestart");

    expect(fresh.getPageReadStats()).toMatchObject({
      requested: 0,
      read: 0,
      readRate: 0,
      averageReadMs: 0,
      excerptKeptRate: 0,
    });
  });

  it("reports how much of a page the budget kept, not how many overflowed", async () => {
    vi.resetModules();
    const fresh = await import("./pageReadsSinceLastRestart");

    // Both pages overflow, so a count of pages that overflowed would read 100%
    // here and say nothing about where the budget sits.
    fresh.recordPageRead({
      outcome: "read",
      durationMs: 10,
      passagesKept: 5,
      passagesAvailable: 100,
    });
    fresh.recordPageRead({
      outcome: "read",
      durationMs: 10,
      passagesKept: 15,
      passagesAvailable: 100,
    });

    expect(fresh.getPageReadStats().excerptKeptRate).toBe(10);
  });

  it("reports the whole page kept when it all fit", async () => {
    vi.resetModules();
    const fresh = await import("./pageReadsSinceLastRestart");

    fresh.recordPageRead({
      outcome: "read",
      durationMs: 10,
      passagesKept: 4,
      passagesAvailable: 4,
    });

    expect(fresh.getPageReadStats().excerptKeptRate).toBe(100);
  });
});