Skip to content
File

Blob: src/cloudflare/internal/test/d1/d1-api-test-with-sessions.js

javascript168 lines
1// Copyright (c) 2023 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5import * as assert from 'node:assert';
6 
7import { itShould, testD1ApiQueriesHappyPath } from './d1-api-test-common';
8 
9const test = (fn, getDB = getDBFromEnv) => ({
10 async test(ctr, env) {
11 await fn(getDB(env), env.d1MockFetcher);
12 },
13});
14 
15function getDBFromEnv(env) {
16 return env.d1;
17}
18 
19export const test_d1_api_happy_path = test(
20 testD1ApiQueriesHappyPath,
21 getDBFromEnv
22);
23 
24export const test_d1_api_happy_path_withsessions_default = test(
25 testD1ApiQueriesHappyPath,
26 (env) => getDBFromEnv(env).withSession()
27);
28 
29export const test_d1_api_happy_path_withsessions_first_unconstrained = test(
30 testD1ApiQueriesHappyPath,
31 (env) => getDBFromEnv(env).withSession('first-unconstrained')
32);
33 
34export const test_d1_api_happy_path_withsessions_first_primary = test(
35 testD1ApiQueriesHappyPath,
36 (env) => getDBFromEnv(env).withSession('first-primary')
37);
38 
39export const test_d1_api_happy_path_withsessions_some_ranomd_token = test(
40 testD1ApiQueriesHappyPath,
41 (env) => getDBFromEnv(env).withSession('token-doesnot-matter-for-now')
42);
43 
44// envD1MockFetcher is the default export worker in `d1-mock.js`, i.e. the `fetch()` entry point.
45const getCommitTokensSentFromBinding = async (envD1MockFetcher) =>
46 (
47 await (
48 await envD1MockFetcher.fetch(`http://d1-api-test/commitTokens`)
49 ).json()
50 ).commitTokensReceived;
51const getCommitTokensReturnedFromEyeball = async (envD1MockFetcher) =>
52 (
53 await (
54 await envD1MockFetcher.fetch(`http://d1-api-test/commitTokens`)
55 ).json()
56 ).commitTokensReturned;
57const resetCommitTokens = async (envD1MockFetcher) =>
58 await (
59 await envD1MockFetcher.fetch(`http://d1-api-test/commitTokens/reset`)
60 ).json();
61const setNextCommitTokenFromEyeball = async (envD1MockFetcher, t) =>
62 await (
63 await envD1MockFetcher.fetch(
64 `http://d1-api-test/commitTokens/nextToken?t=${t}`
65 )
66 ).json();
67 
68export const test_d1_api_withsessions_token_handling = test(
69 testD1ApiWithSessionsTokensHandling,
70 getDBFromEnv
71);
72 
73async function testD1ApiWithSessionsTokensHandling(DB, envD1MockFetcher) {
74 const assertTokensSentReceived = async (firstTokenFromBinding) => {
75 const tokens = await getCommitTokensSentFromBinding(envD1MockFetcher);
76 assert.deepEqual(tokens[0], firstTokenFromBinding);
77 // Make sure we sent back whatever we received from the previous query.
78 assert.deepEqual(
79 tokens.slice(1),
80 (await getCommitTokensReturnedFromEyeball(envD1MockFetcher)).slice(0, -1)
81 );
82 };
83 
84 // Assert tokens sent by the top level DB are always primary!
85 await resetCommitTokens(envD1MockFetcher);
86 await testD1ApiQueriesHappyPath(DB);
87 let tokens = await getCommitTokensSentFromBinding(envD1MockFetcher);
88 assert.deepEqual(
89 tokens.every((t) => t === 'first-primary'),
90 true
91 );
92 // Make sure we received different tokens, and still sent first-primary.
93 assert.deepEqual(
94 (await getCommitTokensReturnedFromEyeball(envD1MockFetcher)).every(
95 (t) => t !== 'first-primary'
96 ),
97 true
98 );
99 
100 // Assert tokens sent by the DEFAULT DB.withSession()
101 await resetCommitTokens(envD1MockFetcher);
102 await testD1ApiQueriesHappyPath(DB.withSession());
103 await assertTokensSentReceived('first-unconstrained');
104 
105 // Assert tokens sent by the DB.withSession("first-unconstrained")
106 await resetCommitTokens(envD1MockFetcher);
107 await testD1ApiQueriesHappyPath(DB.withSession('first-unconstrained'));
108 await assertTokensSentReceived('first-unconstrained');
109 
110 // Assert tokens sent by the DB.withSession("first-primary")
111 await resetCommitTokens(envD1MockFetcher);
112 await testD1ApiQueriesHappyPath(DB.withSession('first-primary'));
113 await assertTokensSentReceived('first-primary');
114}
115 
116export const test_d1_api_withsessions_old_token_skipped = test(
117 testD1ApiWithSessionsOldTokensSkipped,
118 getDBFromEnv
119);
120 
121async function testD1ApiWithSessionsOldTokensSkipped(DB, envD1MockFetcher) {
122 const runTest = async (session) => {
123 await resetCommitTokens(envD1MockFetcher);
124 await session.prepare(`SELECT * FROM sqlite_master;`).all();
125 await session.prepare(`SELECT * FROM sqlite_master;`).all();
126 
127 // This is alphanumerically smaller than the tokens generated by d1-mock.js.
128 await setNextCommitTokenFromEyeball(envD1MockFetcher, '------');
129 
130 // The token from this should be ignored!
131 await session.prepare(`SELECT * FROM sqlite_master;`).all();
132 
133 // But not from this since a normal larger value should be received.
134 await session.prepare(`SELECT * FROM sqlite_master;`).all();
135 await session.prepare(`SELECT * FROM sqlite_master;`).all();
136 
137 const tokensFromBinding =
138 await getCommitTokensSentFromBinding(envD1MockFetcher);
139 const tokensFromEyeball =
140 await getCommitTokensReturnedFromEyeball(envD1MockFetcher);
141 const expectedTokensFromBinding = [
142 'first-unconstrained',
143 tokensFromEyeball[0],
144 tokensFromEyeball[1],
145 // We skip the commit token "------", since the previously received one was more recent.
146 tokensFromEyeball[1],
147 // The binding then sends back the next largest value.
148 tokensFromEyeball[3],
149 ];
150 assert.deepEqual(tokensFromBinding, expectedTokensFromBinding);
151 
152 return { ok: true };
153 };
154 
155 itShould('default DB', runTest(DB), { ok: true });
156 itShould('withSession()', runTest(DB.withSession()), { ok: true });
157 itShould(
158 'withSession(first-unconstrained)',
159 runTest(DB.withSession('first-unconstrained')),
160 { ok: true }
161 );
162 itShould(
163 'withSession(first-primary)',
164 runTest(DB.withSession('first-primary')),
165 { ok: true }
166 );
167}