• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

AJGranowski / preceding-tag-action / 25259602307

02 May 2026 07:08PM UTC coverage: 93.305% (-0.4%) from 93.711%
25259602307

Pull #77

github

AJGranowski
Fix fuzz test
Pull Request #77: Add request cache

172 of 183 branches covered (93.99%)

Branch coverage included in aggregate %.

163 of 176 new or added lines in 4 files covered. (92.61%)

4 existing lines in 2 files now uncovered.

483 of 519 relevant lines covered (93.06%)

12.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.0
/src/ETagRequestCacheDB.ts
1
import { DatabaseSync as SQLiteDB } from "node:sqlite";
1✔
2
import type { SQLOutputValue } from "node:sqlite";
3
import { join as pathJoin } from "path";
1✔
4

5
import type { CachedResponse } from "./types/CachedResponse";
6

7
const CACHE_DIR = pathJoin("/", "tmp", ".cache", "preceding-tag-action");
1✔
8
const REQUEST_CACHE_FILE = "request-cache.sqlite3";
1✔
9

10
/**
11
 * ```
12
 * const cache = new CacheDB();
13
 * cache.open();
14
 * cache.queryETag(...);
15
 * cache.queryResponse(...); // on cache hit
16
 * cache.insertRequestResponse(...); // on cache miss
17
 * cache.close();
18
 * ```
19
 */
20
class ETagRequestCacheDB {
1✔
21
    private readonly db: SQLiteDB;
1✔
22

23
    constructor(db = new SQLiteDB(pathJoin(CACHE_DIR, REQUEST_CACHE_FILE), {open: false})) {
1✔
24
        this.db = db;
14✔
25
    }
14✔
26

27
    /**
28
     * Delete invalidated SHAs and cascade deletions to foreign keys.
29
     */
30
    public async close(): Promise<void> {
1✔
31
        this.db.close();
3✔
32
    }
3✔
33

34
    public async isOpen(): Promise<boolean> {
1✔
35
        return this.db.isOpen;
14✔
36
    }
14✔
37

38
    /**
39
     * Query the ETag related with the given request hash.
40
     * Returns null if an ETag does not exist.
41
     */
42
    public async matchETag(requestHash: string): Promise<string | null> {
1✔
43
        const statement = this.db.prepare("SELECT etag FROM request_response WHERE request_hash=? LIMIT 1");
3✔
44
        const result = statement.get(requestHash);
3✔
45
        if (result == null || result["etag"] == null) {
3✔
46
            return null;
1✔
47
        }
1✔
48

49
        return result["etag"].toString();
2✔
50
    }
3✔
51

52
    /**
53
     * Query the cached response associated with the given ETag.
54
     * Returns null if a cached response does not exist.
55
     */
56
    public async matchResponse(eTag: string): Promise<CachedResponse | null> {
1✔
57
        const statement = this.db.prepare("SELECT response, timestamp_z_ms FROM request_response WHERE etag=? LIMIT 1");
3✔
58
        const result = statement.get(eTag);
3✔
59
        if (result == null || result["response"] == null || result["timestamp_z_ms"] == null) {
3✔
60
            return null;
1✔
61
        }
1✔
62

63
        return {
2✔
64
            timestampZMS: this.sqlOutputValueToInteger(result["timestamp_z_ms"])!,
2✔
65
            response: JSON.parse(result["response"].toString())
2✔
66
        };
2✔
67
    }
3✔
68

69
    /**
70
     * Open the database, initialize tables, and unmark all SHAs previously marked as tags.
71
     */
72
    public async open(): Promise<void> {
1✔
73
        this.db.open();
5✔
74
        this.initialize();
5✔
75
    }
5✔
76

77
    /**
78
     * Insert a network request & response.
79
     */
80
    public async put(requestHash: string, eTag: string, response: object, timestampZMS: number): Promise<void> {
1✔
81
        const pruneExisting = this.db.prepare("DELETE FROM request_response WHERE request_hash=? OR etag=?");
3✔
82
        const statement = this.db.prepare("INSERT INTO request_response (request_hash, etag, response, timestamp_z_ms) VALUES (?, ?, ?, ?)");
3✔
83
        pruneExisting.run(requestHash, eTag);
3✔
84
        statement.run(requestHash, eTag, JSON.stringify(response), Math.round(timestampZMS));
3✔
85
    }
3✔
86

87
    /**
88
     * Initialize tables if they don't exist.
89
     */
90
    private async initialize(): Promise<void> {
1✔
91
        this.db.exec(
5✔
92
            "CREATE TABLE IF NOT EXISTS request_response(" +
5✔
93
            "request_hash TEXT UNIQUE NOT NULL," +
94
            "etag TEXT UNIQUE NOT NULL," +
95
            "response TEXT NOT NULL," +
96
            "timestamp_z_ms INTEGER," +
97
            "PRIMARY KEY (request_hash, etag)" +
98
            ") STRICT"
99
        );
5✔
100
    }
5✔
101

102
    /**
103
     * Parse the SQL Output into a JavaScript primitive
104
     */
105
    private sqlOutputValueToInteger(value: Exclude<SQLOutputValue, null>): number {
1✔
106
        if (typeof value === "number") {
2✔
107
            return value;
2✔
108
        }
2!
109

NEW
110
        if (typeof value === "string") {
×
NEW
111
            const result = Number.parseFloat(value);
×
NEW
112
            if (result.toString() === value) {
×
NEW
113
                return result;
×
NEW
114
            }
×
NEW
115
        }
×
116

NEW
117
        throw new TypeError(`Unexpected type "${typeof value}" returned by SQLite`);
×
118
    }
2✔
119
}
1✔
120

121
export { ETagRequestCacheDB };
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc