@aidc-toolkit/gs1 1.0.32-beta → 1.0.34-beta

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.
Files changed (51) hide show
  1. package/README.md +290 -21
  2. package/dist/character-set.d.ts.map +1 -1
  3. package/dist/character-set.js +44 -11
  4. package/dist/character-set.js.map +1 -1
  5. package/dist/gcp-length-cache.d.ts +54 -15
  6. package/dist/gcp-length-cache.d.ts.map +1 -1
  7. package/dist/gcp-length-cache.js +181 -45
  8. package/dist/gcp-length-cache.js.map +1 -1
  9. package/dist/gcp-length-data.d.ts +74 -20
  10. package/dist/gcp-length-data.d.ts.map +1 -1
  11. package/dist/gcp-length-data.js +39 -15
  12. package/dist/gcp-length-data.js.map +1 -1
  13. package/dist/gcp-length.d.ts +1 -1
  14. package/dist/gcp-length.d.ts.map +1 -1
  15. package/dist/gcp-length.js +5 -6
  16. package/dist/gcp-length.js.map +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/locale/en/locale-resources.d.ts +39 -36
  22. package/dist/locale/en/locale-resources.d.ts.map +1 -1
  23. package/dist/locale/en/locale-resources.js +4 -1
  24. package/dist/locale/en/locale-resources.js.map +1 -1
  25. package/dist/locale/fr/locale-resources.d.ts +39 -36
  26. package/dist/locale/fr/locale-resources.d.ts.map +1 -1
  27. package/dist/locale/fr/locale-resources.js +4 -1
  28. package/dist/locale/fr/locale-resources.js.map +1 -1
  29. package/dist/locale/i18n.d.ts +6 -3
  30. package/dist/locale/i18n.d.ts.map +1 -1
  31. package/dist/locale/i18n.js +8 -6
  32. package/dist/locale/i18n.js.map +1 -1
  33. package/dist/numeric-identifier-validator.d.ts.map +1 -1
  34. package/dist/numeric-identifier-validator.js.map +1 -1
  35. package/dist/variable-measure.d.ts +4 -4
  36. package/dist/variable-measure.js +4 -4
  37. package/package.json +12 -10
  38. package/src/character-set.ts +55 -11
  39. package/src/gcp-length-cache.ts +208 -54
  40. package/src/gcp-length-data.ts +95 -27
  41. package/src/gcp-length.ts +5 -43
  42. package/src/index.ts +2 -1
  43. package/src/locale/en/locale-resources.ts +5 -2
  44. package/src/locale/fr/locale-resources.ts +5 -2
  45. package/src/locale/i18n.ts +9 -7
  46. package/src/locale/i18next.d.ts +2 -0
  47. package/src/numeric-identifier-validator.ts +1 -1
  48. package/src/variable-measure.ts +4 -4
  49. package/test/character-set.test.ts +46 -0
  50. package/test/gcp-length.test.ts +179 -240
  51. package/tsconfig-src.tsbuildinfo +1 -1
@@ -37,12 +37,12 @@ export class VariableMeasure {
37
37
  *
38
38
  * Some examples:
39
39
  *
40
- * - 2IIIIIVPPPPC - RCN-12 with a five-digit item reference, a price or weight check digit, and a four-digit price
40
+ * - `2IIIIIVPPPPC` - RCN-12 with a five-digit item reference, a price or weight check digit, and a four-digit price
41
41
  * or weight.
42
- * - 23IIIIVPPPPPC - RCN-13 with a four-digit item reference, a price or weight check digit, and a five-digit price
42
+ * - `23IIIIVPPPPPC` - RCN-13 with a four-digit item reference, a price or weight check digit, and a five-digit price
43
43
  * or weight.
44
- * - 2IIIIIIPPPPC - RCN-12 with a six-digit item reference and a four-digit price or eight.
45
- * - 29IIIIIPPPPPC - RCN-13 with a five-digit item reference and a five-digit price or weight.
44
+ * - `2IIIIIIPPPPC` - RCN-12 with a six-digit item reference and a four-digit price or eight.
45
+ * - `29IIIIIPPPPPC` - RCN-13 with a five-digit item reference and a five-digit price or weight.
46
46
  *
47
47
  * @param format
48
48
  * Format.
@@ -0,0 +1,46 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { AI64_VALIDATOR } from "../src/index.js";
3
+
4
+ describe("AI 64 character set validator", () => {
5
+ test("Character set", () => {
6
+ expect(() => {
7
+ AI64_VALIDATOR.validate("-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz");
8
+ }).not.toThrow(RangeError);
9
+
10
+ expect(() => {
11
+ AI64_VALIDATOR.validate("-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxy");
12
+ }).toThrow("Length 63 must be a multiple of 4");
13
+
14
+ expect(() => {
15
+ AI64_VALIDATOR.validate("-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwx");
16
+ }).toThrow("Length 62 must be a multiple of 4");
17
+
18
+ expect(() => {
19
+ AI64_VALIDATOR.validate("-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvw");
20
+ }).toThrow("Length 61 must be a multiple of 4");
21
+
22
+ expect(() => {
23
+ AI64_VALIDATOR.validate("-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyzABC=");
24
+ }).not.toThrow(RangeError);
25
+
26
+ expect(() => {
27
+ AI64_VALIDATOR.validate("-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyzAB==");
28
+ }).not.toThrow(RangeError);
29
+
30
+ expect(() => {
31
+ AI64_VALIDATOR.validate("-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyzA===");
32
+ }).toThrow("Invalid character '=' at position 65");
33
+
34
+ expect(() => {
35
+ AI64_VALIDATOR.validate("-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz====");
36
+ }).toThrow("Invalid character '=' at position 64");
37
+
38
+ expect(() => {
39
+ AI64_VALIDATOR.validate("=-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxy");
40
+ }).toThrow("Invalid character '=' at position 0");
41
+
42
+ expect(() => {
43
+ AI64_VALIDATOR.validate("-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ=_abcdefghijklmnopqrstuvwxy");
44
+ }).toThrow("Invalid character '=' at position 37");
45
+ });
46
+ });
@@ -1,163 +1,107 @@
1
- import type { Promisable } from "@aidc-toolkit/core";
1
+ import { type AppDataStorage, LocalAppDataStorage } from "@aidc-toolkit/core";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
- import { describe, expect, test } from "vitest";
4
+ import { afterAll, beforeAll, describe, expect, test } from "vitest";
5
5
  import * as GCPLength from "../src/gcp-length.js";
6
6
  import {
7
7
  GCPLengthCache,
8
8
  type GCPLengthData,
9
+ type GCPLengthHeader,
10
+ type GCPLengthSourceJSON,
9
11
  IdentifierTypes,
12
+ isGCPLengthHeader,
13
+ isGCPLengthSourceJSON,
10
14
  PrefixManager,
11
15
  PrefixTypes,
12
- PrefixValidator
16
+ PrefixValidator,
17
+ RemoteGCPLengthCache
13
18
  } from "../src/index.js";
14
19
 
15
- interface ReadonlyStorage<T> {
16
- read: () => T;
17
- }
18
-
19
- interface Storage<T> extends ReadonlyStorage<T> {
20
- write: (value: NonNullable<T>) => void;
21
- }
22
-
23
- interface RequiredStorage<T> extends Storage<T> {
24
- read: () => NonNullable<T>;
25
- }
20
+ const DATA_DIRECTORY = "test/data";
26
21
 
27
- function makeReadonly<T>(storage: Storage<T | undefined>): ReadonlyStorage<T> {
28
- return {
29
- read(): T {
30
- const t = storage.read();
22
+ class GCPLengthCacheJSONSource extends GCPLengthCache {
23
+ readonly #pathKey: string;
31
24
 
32
- if (t === undefined) {
33
- throw new Error("Storage has no data");
34
- }
25
+ #gcpLengthSourceJSON!: GCPLengthSourceJSON;
35
26
 
36
- return t;
37
- }
38
- };
39
- }
27
+ constructor(appDataStorage: AppDataStorage<boolean>, index: number) {
28
+ super(appDataStorage);
40
29
 
41
- function makeRequired<T>(storage: Storage<T | undefined>): RequiredStorage<T> {
42
- return {
43
- read(): NonNullable<T> {
44
- const t = storage.read();
30
+ this.#pathKey = `gcpprefixformatlist-${index}`;
31
+ }
45
32
 
46
- if (t === undefined || t === null) {
47
- throw new Error("Storage has no data");
33
+ get sourceDateTime(): Promise<Date> {
34
+ return this.appDataStorage.read(this.#pathKey).then((appData) => {
35
+ if (!isGCPLengthSourceJSON(appData)) {
36
+ throw new Error(`File ${this.#pathKey}.json has invalid content`);
48
37
  }
49
38
 
50
- return t;
51
- },
39
+ this.#gcpLengthSourceJSON = appData;
52
40
 
53
- write: storage.write
54
- };
41
+ return new Date(appData.GCPPrefixFormatList.date);
42
+ });
43
+ }
44
+
45
+ get sourceData(): GCPLengthSourceJSON {
46
+ return this.#gcpLengthSourceJSON;
47
+ }
55
48
  }
56
49
 
57
- function createGCPLengthCache(nextCheckDateTimeStorage: Storage<Date | undefined>, cacheDataStorage: Storage<GCPLengthData | undefined>, sourceDateTimeStorage: ReadonlyStorage<Date>, sourceDataStorage: ReadonlyStorage<GCPLengthData> | ReadonlyStorage<string>): GCPLengthCache {
58
- return new class extends GCPLengthCache {
59
- get nextCheckDateTime(): Promisable<Date | undefined> {
60
- return nextCheckDateTimeStorage.read();
61
- }
50
+ class GCPLengthCacheBinarySource extends GCPLengthCache {
51
+ readonly #headerPathKey: string;
62
52
 
63
- get cacheDateTime(): Date | undefined {
64
- return cacheDataStorage.read()?.dateTime;
65
- }
53
+ readonly #dataPathKey: string;
66
54
 
67
- get cacheData(): GCPLengthData {
68
- return makeRequired(cacheDataStorage).read();
69
- }
55
+ #gcpLengthHeader!: GCPLengthHeader;
70
56
 
71
- get sourceDateTime(): Date {
72
- return sourceDateTimeStorage.read();
73
- }
57
+ constructor(appDataStorage: AppDataStorage<boolean>, index: number) {
58
+ super(appDataStorage);
74
59
 
75
- get sourceData(): GCPLengthData | string {
76
- return sourceDataStorage.read();
77
- }
60
+ this.#headerPathKey = `${GCPLengthCache.HEADER_STORAGE_KEY}-${index}`;
61
+ this.#dataPathKey = `${GCPLengthCache.DATA_STORAGE_KEY}-${index}`;
62
+ }
78
63
 
79
- update(nextCheckDateTime: Date, _cacheDateTime?: Date, cacheData?: GCPLengthData): void {
80
- nextCheckDateTimeStorage.write(nextCheckDateTime);
64
+ get sourceDateTime(): Promise<Date> {
65
+ return this.appDataStorage.read(this.#headerPathKey).then((appData) => {
66
+ if (!isGCPLengthHeader(appData)) {
67
+ throw new Error(`File ${this.#headerPathKey}.json not found or has invalid content`);
68
+ }
69
+
70
+ this.#gcpLengthHeader = appData;
71
+
72
+ return appData.dateTime;
73
+ });
74
+ }
81
75
 
82
- if (cacheData !== undefined) {
83
- cacheDataStorage.write(cacheData);
76
+ get sourceData(): Promise<GCPLengthData> {
77
+ return this.appDataStorage.read(this.#dataPathKey, true).then((appData) => {
78
+ if (!(appData instanceof Uint8Array)) {
79
+ throw new Error(`File ${this.#dataPathKey}.bin not found or has invalid content`);
84
80
  }
85
- }
86
- }();
81
+
82
+ return {
83
+ ...this.#gcpLengthHeader,
84
+ data: appData
85
+ };
86
+ });
87
+ }
87
88
  }
88
89
 
89
- const DATA_DIRECTORY = "test/data";
90
-
91
- const BINARY_1_HEADER_PATH = path.resolve(DATA_DIRECTORY, "gcp-length-header-1.txt");
90
+ const NEXT_CHECK_DATE_TIME_PATH = path.resolve(DATA_DIRECTORY, "gcp-length-next-check-date-time.json");
92
91
 
93
- const BINARY_1_DATA_PATH = path.resolve(DATA_DIRECTORY, "gcp-length-data-1.bin");
92
+ const BINARY_HEADER_PATH = path.resolve(DATA_DIRECTORY, "gcp-length-header.json");
94
93
 
95
- const BINARY_2_HEADER_PATH = path.resolve(DATA_DIRECTORY, "gcp-length-header-2.txt");
94
+ const BINARY_DATA_PATH = path.resolve(DATA_DIRECTORY, "gcp-length-data.bin");
96
95
 
97
- const BINARY_2_DATA_PATH = path.resolve(DATA_DIRECTORY, "gcp-length-data-2.bin");
96
+ const BINARY_1_HEADER_PATH = path.resolve(DATA_DIRECTORY, "gcp-length-header-1.json");
98
97
 
99
- const JSON_1_DATA_PATH = path.resolve(DATA_DIRECTORY, "gcpprefixformatlist-1.json");
98
+ const BINARY_1_DATA_PATH = path.resolve(DATA_DIRECTORY, "gcp-length-data-1.bin");
100
99
 
101
- const JSON_2_DATA_PATH = path.resolve(DATA_DIRECTORY, "gcpprefixformatlist-2.json");
100
+ const BINARY_2_HEADER_PATH = path.resolve(DATA_DIRECTORY, "gcp-length-header-2.json");
102
101
 
103
- interface GCPLengthJSON {
104
- _disclaimer: string[];
105
- GCPPrefixFormatList: {
106
- date: string;
107
- entry: Array<{
108
- prefix: string;
109
- gcpLength: number;
110
- }>;
111
- };
112
- }
102
+ const BINARY_2_DATA_PATH = path.resolve(DATA_DIRECTORY, "gcp-length-data-2.bin");
113
103
 
114
104
  describe("GS1 Company Prefix length", () => {
115
- function binaryDataStorage(binaryHeaderPath: string, binaryDataPath: string): Storage<GCPLengthData | undefined> {
116
- return {
117
- read(): GCPLengthData | undefined {
118
- let header: string[] | undefined;
119
- let data: Uint8Array | undefined;
120
-
121
- try {
122
- header = fs.readFileSync(binaryHeaderPath).toString().split("\n");
123
- data = fs.readFileSync(binaryDataPath);
124
- } catch {
125
- header = undefined;
126
- data = undefined;
127
- }
128
-
129
- return header !== undefined && data !== undefined ?
130
- {
131
- dateTime: new Date(header[0]),
132
- disclaimer: `${header.slice(1).join("\n")}\n`,
133
- data
134
- } :
135
- undefined;
136
- },
137
-
138
- write(value: GCPLengthData): void {
139
- fs.writeFileSync(binaryHeaderPath, `${value.dateTime.toISOString()}\n${value.disclaimer}`);
140
- fs.writeFileSync(binaryDataPath, value.data);
141
- }
142
- };
143
- }
144
-
145
- function jsonDateTimeStorage(jsonDateTimePath: string): ReadonlyStorage<Date> {
146
- return {
147
- read(): Date {
148
- return new Date((JSON.parse(fs.readFileSync(jsonDateTimePath).toString()) as GCPLengthJSON).GCPPrefixFormatList.date);
149
- }
150
- };
151
- }
152
-
153
- function jsonDataStorage(jsonDataPath: string): ReadonlyStorage<string> {
154
- return {
155
- read(): string {
156
- return fs.readFileSync(jsonDataPath).toString();
157
- }
158
- };
159
- }
160
-
161
105
  function verifyEqual(prefix: string, node1: GCPLength.Node | undefined, node2: GCPLength.Node | undefined): void {
162
106
  if (node1 !== undefined) {
163
107
  if (node2 === undefined) {
@@ -184,123 +128,113 @@ describe("GS1 Company Prefix length", () => {
184
128
  }
185
129
  }
186
130
 
187
- function unlink(path: string): void {
188
- if (fs.existsSync(path)) {
189
- fs.unlinkSync(path);
131
+ function rm(...paths: string[]): void {
132
+ for (const path of paths) {
133
+ fs.rmSync(path, {
134
+ force: true
135
+ });
190
136
  }
191
137
  }
192
138
 
193
- function unlinkBinaries(): void {
194
- unlink(BINARY_1_HEADER_PATH);
195
- unlink(BINARY_1_DATA_PATH);
196
- unlink(BINARY_2_HEADER_PATH);
197
- unlink(BINARY_2_DATA_PATH);
139
+ function rmCache(): void {
140
+ rm(NEXT_CHECK_DATE_TIME_PATH,
141
+ BINARY_HEADER_PATH, BINARY_DATA_PATH,
142
+ BINARY_1_HEADER_PATH, BINARY_1_DATA_PATH,
143
+ BINARY_2_HEADER_PATH, BINARY_2_DATA_PATH
144
+ );
198
145
  }
199
146
 
200
- const binary1DataStorage = binaryDataStorage(BINARY_1_HEADER_PATH, BINARY_1_DATA_PATH);
201
- const binary2DataStorage = binaryDataStorage(BINARY_2_HEADER_PATH, BINARY_2_DATA_PATH);
202
- const json1DateTimeStorage = jsonDateTimeStorage(JSON_1_DATA_PATH);
203
- const json1DataStorage = jsonDataStorage(JSON_1_DATA_PATH);
204
- const json2DateTimeStorage = jsonDateTimeStorage(JSON_2_DATA_PATH);
205
- const json2DataStorage = jsonDataStorage(JSON_2_DATA_PATH);
206
-
207
- const emptyStorage: Storage<Date | undefined> & Storage<string | undefined> & Storage<GCPLengthData | undefined> = {
208
- read(): undefined {
209
- return undefined;
210
- },
211
-
212
- write() {
213
- }
214
- };
147
+ function save(fileName: string, index: number): void {
148
+ fs.renameSync(fileName, fileName.replace(".", `-${index}.`));
149
+ }
215
150
 
216
- test("Load", async () => {
217
- unlinkBinaries();
151
+ function saveBinary(index: number): void {
152
+ save(BINARY_HEADER_PATH, index);
153
+ save(BINARY_DATA_PATH, index);
154
+ }
218
155
 
219
- let nextCheckDateTime: Date | undefined = undefined;
156
+ function restore(fileName: string, index: number): void {
157
+ fs.renameSync(fileName.replace(".", `-${index}.`), fileName);
158
+ }
220
159
 
221
- const nextCheckDateTimeStorage: Storage<Date | undefined> = {
222
- read(): Date | undefined {
223
- return nextCheckDateTime;
224
- },
160
+ function restoreBinary(index: number): void {
161
+ restore(BINARY_HEADER_PATH, index);
162
+ restore(BINARY_DATA_PATH, index);
163
+ }
225
164
 
226
- write(value: Date): void {
227
- nextCheckDateTime = value;
228
- }
229
- };
165
+ beforeAll(rmCache);
166
+ afterAll(rmCache);
230
167
 
231
- const binary2DataReadonlyStorage = makeReadonly(binary2DataStorage);
232
- const binary2DateTimeReadonlyStorage: ReadonlyStorage<Date> = {
233
- read(): Date {
234
- return binary2DataReadonlyStorage.read().dateTime;
235
- }
236
- };
237
-
238
- // Neither binary available.
239
- await expect((async () => {
240
- await GCPLength.loadData(createGCPLengthCache(nextCheckDateTimeStorage, binary1DataStorage, binary2DateTimeReadonlyStorage, binary2DataReadonlyStorage));
241
- })()).rejects.toThrowError("Storage has no data");
168
+ test("Load", async () => {
169
+ const appDataStorage = new (await LocalAppDataStorage)(DATA_DIRECTORY);
242
170
 
243
- expect(nextCheckDateTime).toBeUndefined();
171
+ const gcpLengthCacheJSON1Source = new GCPLengthCacheJSONSource(appDataStorage, 1);
172
+ const gcpLengthCacheJSON2Source = new GCPLengthCacheJSONSource(appDataStorage, 2);
173
+ const gcpLengthCacheBinary1Source = new GCPLengthCacheBinarySource(appDataStorage, 1);
174
+ const gcpLengthCacheBinary2Source = new GCPLengthCacheBinarySource(appDataStorage, 2);
244
175
 
245
176
  let root1: GCPLength.Root | undefined;
177
+ let root2: GCPLength.Root | undefined;
178
+ let nextCheckDateTime: Date | undefined;
246
179
 
247
- // Binary 1 not available, JSON 1 available.
248
- await expect((async () => {
249
- root1 = await GCPLength.loadData(createGCPLengthCache(nextCheckDateTimeStorage, binary1DataStorage, json1DateTimeStorage, json1DataStorage));
250
- })()).resolves.not.toThrowError(RangeError);
180
+ // Binary not available, JSON 1 available.
181
+ await expect(GCPLength.loadData(gcpLengthCacheJSON1Source).then((root) => {
182
+ root1 = root;
183
+ })).resolves.not.toThrowError(RangeError);
251
184
 
252
- expect(nextCheckDateTime).not.toBeUndefined();
253
-
254
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Known not to be undefined.
255
- expect(new Date().getTime() + 24 * 60 * 60 * 1000 - nextCheckDateTime!.getTime()).toBeLessThan(10000);
185
+ nextCheckDateTime = await gcpLengthCacheJSON1Source.nextCheckDateTime;
256
186
 
257
187
  expect(root1).not.toBeUndefined();
188
+ expect(nextCheckDateTime).not.toBeUndefined();
258
189
 
259
- let root2: GCPLength.Root | undefined;
190
+ // Check next check date/time is a day from now.
191
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Known to be defined.
192
+ expect(new Date().getTime() + 24 * 60 * 60 * 1000 - nextCheckDateTime!.getTime()).toBeLessThan(10000);
260
193
 
261
- // Binary 1 available, binary 2 not available but not in next check date/time window.
262
- await expect((async () => {
263
- root2 = await GCPLength.loadData(createGCPLengthCache(nextCheckDateTimeStorage, binary1DataStorage, binary2DateTimeReadonlyStorage, binary2DataReadonlyStorage));
264
- })()).resolves.not.toThrowError(RangeError);
194
+ // Binary available, binary 1 not available but not in next check date/time window.
195
+ await expect(GCPLength.loadData(gcpLengthCacheBinary1Source).then((root) => {
196
+ root2 = root;
197
+ })).resolves.not.toThrowError(RangeError);
265
198
 
266
199
  expect(root2).not.toBeUndefined();
267
200
 
268
- // Load from JSON 1 equal to load from binary 1.
201
+ // Load from JSON 1 equal to load from binary.
269
202
  expect(() => {
270
203
  verifyEqual("", root1, root2);
271
204
  }).not.toThrow(RangeError);
272
205
 
273
- // eslint-disable-next-line require-atomic-updates -- No rance condition.
274
- nextCheckDateTime = undefined;
275
- root2 = undefined;
206
+ saveBinary(1);
207
+ rm(NEXT_CHECK_DATE_TIME_PATH);
276
208
 
277
- // Binary 2 not available, JSON 2 available.
278
- await expect((async () => {
279
- root2 = await GCPLength.loadData(createGCPLengthCache(nextCheckDateTimeStorage, binary2DataStorage, json2DateTimeStorage, json2DataStorage));
280
- })()).resolves.not.toThrowError(RangeError);
209
+ // Binary not available, JSON 2 available.
210
+ await expect(GCPLength.loadData(gcpLengthCacheJSON2Source).then((root) => {
211
+ root2 = root;
212
+ })).resolves.not.toThrowError(RangeError);
213
+
214
+ nextCheckDateTime = await gcpLengthCacheJSON2Source.nextCheckDateTime;
281
215
 
282
- expect(nextCheckDateTime).not.toBeUndefined();
283
216
  expect(root2).not.toBeUndefined();
217
+ expect(nextCheckDateTime).not.toBeUndefined();
284
218
 
285
- root2 = undefined;
219
+ saveBinary(2);
220
+ restoreBinary(1);
286
221
 
287
- // Binary 1 available, binary 2 available and more recent but not in next check date/time window.
288
- await expect((async () => {
289
- root2 = await GCPLength.loadData(createGCPLengthCache(nextCheckDateTimeStorage, binary1DataStorage, binary2DateTimeReadonlyStorage, binary2DataReadonlyStorage));
290
- })()).resolves.not.toThrowError(RangeError);
222
+ // Binary available, binary 2 available and more recent but not in next check date/time window.
223
+ await expect(GCPLength.loadData(gcpLengthCacheBinary2Source).then((root) => {
224
+ root2 = root;
225
+ })).resolves.not.toThrowError(RangeError);
291
226
 
292
227
  // No change.
293
228
  expect(() => {
294
229
  verifyEqual("", root1, root2);
295
230
  }).not.toThrow(RangeError);
296
231
 
297
- // eslint-disable-next-line require-atomic-updates -- No rance condition.
298
- nextCheckDateTime = new Date();
232
+ await gcpLengthCacheBinary2Source.update(new Date());
299
233
 
300
234
  // Binary 1 available, binary 2 available and more recent and in next check date/time window.
301
- await expect((async () => {
302
- root2 = await GCPLength.loadData(createGCPLengthCache(nextCheckDateTimeStorage, binary1DataStorage, binary2DateTimeReadonlyStorage, binary2DataReadonlyStorage));
303
- })()).resolves.not.toThrowError(RangeError);
235
+ await expect(GCPLength.loadData(gcpLengthCacheBinary2Source).then((root) => {
236
+ root2 = root;
237
+ })).resolves.not.toThrowError(RangeError);
304
238
 
305
239
  expect(root2).not.toBeUndefined();
306
240
 
@@ -308,12 +242,24 @@ describe("GS1 Company Prefix length", () => {
308
242
  expect(() => {
309
243
  verifyEqual("", root1, root2);
310
244
  }).toThrow(RangeError);
311
-
312
- unlinkBinaries();
313
245
  });
314
246
 
315
247
  test("Identifiers", async () => {
316
- const tempRoot = await GCPLength.loadData(createGCPLengthCache(emptyStorage, emptyStorage, json1DateTimeStorage, json1DataStorage));
248
+ const gcpLengthCacheJSON2Source = new class extends GCPLengthCacheJSONSource {
249
+ override get nextCheckDateTime(): Promise<Date | undefined> {
250
+ return Promise.resolve(undefined);
251
+ }
252
+
253
+ override get cacheDateTime(): Promise<Date | undefined> {
254
+ return Promise.resolve(undefined);
255
+ }
256
+
257
+ override async update(): Promise<void> {
258
+ return Promise.resolve();
259
+ }
260
+ }(new (await LocalAppDataStorage)(DATA_DIRECTORY), 2);
261
+
262
+ const tempRoot = await GCPLength.loadData(gcpLengthCacheJSON2Source);
317
263
 
318
264
  expect(tempRoot).not.toBeUndefined();
319
265
 
@@ -333,7 +279,7 @@ describe("GS1 Company Prefix length", () => {
333
279
  }
334
280
  }
335
281
 
336
- const gcpLengthJSON = JSON.parse(json1DataStorage.read()) as GCPLengthJSON;
282
+ const gcpLengthJSON = gcpLengthCacheJSON2Source.sourceData;
337
283
  const entries = gcpLengthJSON.GCPPrefixFormatList.entry;
338
284
 
339
285
  for (let i = 0; i < 1000; i++) {
@@ -366,40 +312,33 @@ describe("GS1 Company Prefix length", () => {
366
312
  }
367
313
  });
368
314
 
369
- // test("Remote", async () => {
370
- // const gcpLengthCache = new class extends RemoteGCPLengthCache {
371
- // #nextCheckDateTime: Date | undefined = undefined;
372
- //
373
- // #cacheData: GCPLengthData | undefined = undefined;
374
- //
375
- // get nextCheckDateTime(): Date | undefined {
376
- // return this.#nextCheckDateTime;
377
- // }
378
- //
379
- // get cacheDateTime(): Date | undefined {
380
- // return this.#cacheData?.dateTime;
381
- // }
382
- //
383
- // get cacheData(): GCPLengthData {
384
- // // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Method will be called only if cache date/time is defined.
385
- // return this.#cacheData!;
386
- // }
387
- //
388
- // update(nextCheckDateTime: Date, _cacheDateTime?: Date, cacheData?: GCPLengthData): Promisable<void> {
389
- // this.#nextCheckDateTime = nextCheckDateTime;
390
- //
391
- // if (cacheData !== undefined) {
392
- // this.#cacheData = cacheData;
393
- // }
394
- // }
395
- // }();
396
- //
397
- // await expect((async () => {
398
- // await GCPLength.loadData(gcpLengthCache);
399
- // })()).resolves.not.toThrowError(RangeError);
400
- //
401
- // expect(gcpLengthCache.nextCheckDateTime).not.toBeUndefined();
402
- // expect(gcpLengthCache.cacheDateTime).not.toBeUndefined();
403
- // expect(gcpLengthCache.cacheData).not.toBeUndefined();
404
- // });
315
+ test("Remote", async () => {
316
+ let savedNextCheckDateTime: Date | undefined = undefined;
317
+ let savedCacheDateTime: Date | undefined = undefined;
318
+ let savedCacheData: GCPLengthData | undefined = undefined;
319
+
320
+ const gcpLengthCache = new class extends RemoteGCPLengthCache {
321
+ override get nextCheckDateTime(): Promise<Date | undefined> {
322
+ return Promise.resolve(savedNextCheckDateTime);
323
+ }
324
+
325
+ override get cacheDateTime(): Promise<Date | undefined> {
326
+ return Promise.resolve(savedCacheDateTime);
327
+ }
328
+
329
+ override async update(nextCheckDateTime: Date, cacheDateTime?: Date, cacheData?: GCPLengthData): Promise<void> {
330
+ savedNextCheckDateTime = nextCheckDateTime;
331
+ savedCacheDateTime = cacheDateTime;
332
+ savedCacheData = cacheData;
333
+
334
+ return Promise.resolve();
335
+ }
336
+ }(new (await LocalAppDataStorage)(DATA_DIRECTORY));
337
+
338
+ await expect(GCPLength.loadData(gcpLengthCache)).resolves.not.toThrowError(RangeError);
339
+
340
+ expect(savedNextCheckDateTime).not.toBeUndefined();
341
+ expect(savedCacheDateTime).not.toBeUndefined();
342
+ expect(savedCacheData).not.toBeUndefined();
343
+ });
405
344
  });