@remit/drizzle-service 0.0.59 → 0.0.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/drizzle-service",
3
- "version": "0.0.59",
3
+ "version": "0.0.61",
4
4
  "description": "Drizzle ORM service over SQLite",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
package/src/id.test.ts ADDED
@@ -0,0 +1,69 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, test } from "node:test";
3
+ import {
4
+ bodyPartContentId,
5
+ bodyPartId,
6
+ bodyPartParameterId,
7
+ deterministicBase36Id,
8
+ envelopeAddressId,
9
+ envelopeId,
10
+ generateDeterministicId,
11
+ rootBodyPartId,
12
+ } from "./id.js";
13
+
14
+ const MESSAGE = "message-golden";
15
+
16
+ describe("derived ids are stored primary keys: a changed value orphans every row already written and needs a migration, never a new expectation here", () => {
17
+ test("generateDeterministicId pins the namespace and the UUIDv5 encoding", () => {
18
+ assert.equal(
19
+ generateDeterministicId("golden-seed"),
20
+ "68dd8eea-ad03-5221-b37c-52bbf1772c30",
21
+ );
22
+ });
23
+
24
+ test("deterministicBase36Id pins the base36 encoding shared with the electrodb adapter", () => {
25
+ assert.equal(
26
+ deterministicBase36Id("golden-seed"),
27
+ "67hxxs95ofdk5frbprqyrgwmo",
28
+ );
29
+ });
30
+
31
+ test("bodyPartId pins the body_part primary key", () => {
32
+ assert.equal(
33
+ bodyPartId(MESSAGE, "1.2"),
34
+ "022d58cc-585c-5391-8106-f2d2c1c0b228",
35
+ );
36
+ });
37
+
38
+ test("rootBodyPartId pins the root MIME node's key, part path 0", () => {
39
+ assert.equal(
40
+ rootBodyPartId(MESSAGE),
41
+ "f15cb8cc-8e03-5d68-812a-70bac87e2261",
42
+ );
43
+ });
44
+
45
+ test("bodyPartParameterId pins the body_part_parameter primary key", () => {
46
+ assert.equal(
47
+ bodyPartParameterId(MESSAGE, "1.2", "charset"),
48
+ "fdac41cc-7cbf-5bf7-bfb3-48f66e2e7f0c",
49
+ );
50
+ });
51
+
52
+ test("envelopeId pins the envelope primary key", () => {
53
+ assert.equal(envelopeId(MESSAGE), "fdfd34a1-84fb-535f-a586-965338211e99");
54
+ });
55
+
56
+ test("envelopeAddressId pins the envelope_address primary key", () => {
57
+ assert.equal(
58
+ envelopeAddressId(MESSAGE, "to", 1),
59
+ "5f4e930e-e418-51a2-9402-657822f8688b",
60
+ );
61
+ });
62
+
63
+ test("bodyPartContentId pins the body_part_content primary key", () => {
64
+ assert.equal(
65
+ bodyPartContentId(MESSAGE, "bodypart-golden"),
66
+ "c0ee5f7d-1894-5957-9f65-d9aafdf306ab",
67
+ );
68
+ });
69
+ });
@@ -1,7 +1,6 @@
1
1
  import assert from "node:assert";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { after, before, describe, test } from "node:test";
4
- import { envelopeAddressId } from "../id.js";
5
4
  import { createTestDb, randomId, type TestDb } from "../test-db.js";
6
5
  import { AddressRepo } from "./i4-address.js";
7
6
 
@@ -140,7 +139,6 @@ describe("AddressRepo", () => {
140
139
  test("createEnvelopeAddress and getEnvelopeAddress", async () => {
141
140
  const messageId = randomUUID();
142
141
  const addressId = randomUUID();
143
- const envId = envelopeAddressId(messageId, "from", 0);
144
142
 
145
143
  const addr = await repo.createAddress({
146
144
  ...makeAddressInput(randomId()),
@@ -155,18 +153,18 @@ describe("AddressRepo", () => {
155
153
  addressOrder: 0,
156
154
  });
157
155
 
158
- assert.equal(ea.envelopeAddressId, envId);
159
156
  assert.equal(ea.messageId, messageId);
160
157
  assert.equal(ea.addressRole, "from");
161
158
 
162
- const fetched = await repo.getEnvelopeAddress(envId);
163
- assert.equal(fetched.envelopeAddressId, envId);
159
+ const fetched = await repo.getEnvelopeAddress(ea.envelopeAddressId);
160
+ assert.equal(fetched.envelopeAddressId, ea.envelopeAddressId);
161
+ assert.equal(fetched.addressId, addressId);
164
162
 
165
- await repo.deleteEnvelopeAddress(envId);
163
+ await repo.deleteEnvelopeAddress(ea.envelopeAddressId);
166
164
  await repo.deleteAddress(addr.accountConfigId, addr.addressId);
167
165
  });
168
166
 
169
- test("upsertEnvelopeAddress is idempotent", async () => {
167
+ test("upsertEnvelopeAddress keys the row on messageId, role and order", async () => {
170
168
  const messageId = randomUUID();
171
169
  const input = {
172
170
  messageId,
@@ -176,14 +174,22 @@ describe("AddressRepo", () => {
176
174
  addressOrder: 1,
177
175
  };
178
176
  const first = await repo.upsertEnvelopeAddress(input);
179
- const second = await repo.upsertEnvelopeAddress(input);
180
- assert.equal(
181
- first.envelopeAddressId,
182
- envelopeAddressId(messageId, "to", 1),
183
- );
177
+ const second = await repo.upsertEnvelopeAddress({
178
+ ...input,
179
+ addressId: randomUUID(),
180
+ normalizedEmail: "y@example.com",
181
+ });
184
182
  assert.equal(second.envelopeAddressId, first.envelopeAddressId);
183
+ assert.equal(second.normalizedEmail, "x@example.com");
184
+
185
+ const reordered = await repo.upsertEnvelopeAddress({
186
+ ...input,
187
+ addressOrder: 2,
188
+ });
189
+ assert.notEqual(reordered.envelopeAddressId, first.envelopeAddressId);
185
190
 
186
191
  await repo.deleteEnvelopeAddress(first.envelopeAddressId);
192
+ await repo.deleteEnvelopeAddress(reordered.envelopeAddressId);
187
193
  });
188
194
 
189
195
  test("deleteManyEnvelopeAddresses removes in batch", async () => {
@@ -135,6 +135,14 @@ describe("MailboxRepo", () => {
135
135
  );
136
136
  });
137
137
 
138
+ test("findByPathPrefix finds nothing in a flat namespace", async () => {
139
+ const accountId = randomId();
140
+ await repo.create(makeMailboxInput(accountId, "Work"));
141
+ await repo.create(makeMailboxInput(accountId, "Workshop"));
142
+
143
+ assert.deepEqual(await repo.findByPathPrefix(accountId, "Work", ""), []);
144
+ });
145
+
138
146
  test("renameChildPaths updates all children", async () => {
139
147
  const accountId = randomId();
140
148
  await repo.create(makeMailboxInput(accountId, "OldName/Sub1"));
@@ -306,6 +306,9 @@ export class MailboxRepo implements IMailboxRepository {
306
306
  pathPrefix: string,
307
307
  delimiter = "/",
308
308
  ): Promise<MailboxItem[]> {
309
+ // A flat namespace nests nothing: with no delimiter the prefix is the
310
+ // folder’s own path, which would match every sibling starting with it.
311
+ if (delimiter.length === 0) return [];
309
312
  const rows = await this.db
310
313
  .select()
311
314
  .from(mailboxTable)
@@ -7,6 +7,7 @@ import { createSqliteTestDb } from "../test-db-sqlite.js";
7
7
  import {
8
8
  clampThreadSearchLimit,
9
9
  DrizzleThreadMessageRepository,
10
+ deriveThreadMessageId,
10
11
  THREAD_SEARCH_MAX_LIMIT,
11
12
  } from "./thread-message.js";
12
13
 
@@ -936,3 +937,12 @@ describe("DrizzleThreadMessageRepository — core CRUD", () => {
936
937
  assert.equal(await repo.countByThread(other, threadId), 0);
937
938
  });
938
939
  });
940
+
941
+ describe("derived ids are stored primary keys: a changed value orphans every row already written and needs a migration, never a new expectation here", () => {
942
+ test("deriveThreadMessageId pins the thread_message primary key", () => {
943
+ assert.equal(
944
+ deriveThreadMessageId("thread-golden", "message-golden"),
945
+ "agmlamuvd9feee9fmfvqxrb35",
946
+ );
947
+ });
948
+ });