@nocobase/app-template-default 1.0.0-beta.85 → 1.0.0-beta.87

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.
@@ -1,4 +1,5 @@
1
- import { expect, it } from 'vitest';
1
+ import { expect, it, vi } from 'vitest';
2
+ import { ConcurrentAbortError } from '@nocobase/concurrency';
2
3
  import { createAppTest } from '@nocobase/app-testing/server';
3
4
  import {
4
5
  concurrencyServiceToken,
@@ -23,6 +24,55 @@ const test = createAppTest({
23
24
  },
24
25
  });
25
26
 
27
+ test('explicit ttl keeps local Lock and Executor mutually exclusive until actual release', async ({
28
+ app,
29
+ }) => {
30
+ const manager = await app.container
31
+ .resolve(concurrencyServiceToken)
32
+ .getManager();
33
+ const lock = await manager.createLock('concurrency-test:ttl', { ttl: 1 });
34
+ const executor = await manager.createSynchronous('concurrency-test:ttl', {
35
+ ttl: 2,
36
+ });
37
+ vi.useFakeTimers();
38
+ try {
39
+ const held = await lock.lock();
40
+ let entered = false;
41
+ const done = Promise.withResolvers<string>();
42
+ const running = executor.run(() => {
43
+ entered = true;
44
+ return done.promise;
45
+ });
46
+ try {
47
+ await vi.advanceTimersByTimeAsync(1000);
48
+ expect(entered).toBe(false);
49
+ await held.release();
50
+ await vi.advanceTimersByTimeAsync(0);
51
+ expect(entered).toBe(true);
52
+ let successorEntered = false;
53
+ const next = lock.lock().then((lease) => {
54
+ successorEntered = true;
55
+ return lease;
56
+ });
57
+ await vi.advanceTimersByTimeAsync(1000);
58
+ expect(successorEntered).toBe(false);
59
+ done.resolve('actual');
60
+ expect(await running).toBe('actual');
61
+ const successor = await next;
62
+ await held.release();
63
+ const attempt = executor.tryRun(() => 'wrong', 10);
64
+ await vi.advanceTimersByTimeAsync(10);
65
+ expect(await attempt).toEqual({ acquired: false });
66
+ await successor.release();
67
+ } finally {
68
+ done.resolve('actual');
69
+ await running;
70
+ await held.release();
71
+ }
72
+ } finally {
73
+ vi.useRealTimers();
74
+ }
75
+ });
26
76
  test('the real default composition supplies one memory lock domain to two business consumers', async ({
27
77
  app,
28
78
  }) => {
@@ -50,6 +100,20 @@ test('the real default composition supplies one memory lock domain to two busine
50
100
  acquired: true,
51
101
  value: 'done',
52
102
  });
103
+ const cancellation = new AbortController();
104
+ const held = await lock.lock();
105
+ const cancelled = lock
106
+ .lock({ signal: cancellation.signal })
107
+ .catch((error: unknown) => error);
108
+ cancellation.abort();
109
+ expect(await cancelled).toBeInstanceOf(ConcurrentAbortError);
110
+ await held.release();
111
+ const active = await lock.lock();
112
+ const interrupted = lock.lock().catch((error: unknown) => error);
113
+ await app.shutdown();
114
+ expect(await interrupted).toBeInstanceOf(ConcurrentAbortError);
115
+ await active.release();
116
+ await expect(lock.lock()).rejects.toBeInstanceOf(ConcurrentAbortError);
53
117
  });
54
118
 
55
119
  it('declares only the default selector environment variable and no public configuration', () => {