@snowtop/ent 0.1.0-alpha90 → 0.1.0-alpha91

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 (63) hide show
  1. package/action/orchestrator.js +5 -1
  2. package/core/clause.d.ts +15 -0
  3. package/core/clause.js +44 -2
  4. package/core/config.js +5 -1
  5. package/core/db.js +5 -1
  6. package/core/ent.js +7 -8
  7. package/core/loaders/assoc_count_loader.d.ts +1 -0
  8. package/core/loaders/assoc_count_loader.js +8 -1
  9. package/core/loaders/assoc_edge_loader.js +5 -1
  10. package/core/loaders/object_loader.js +5 -1
  11. package/core/loaders/query_loader.js +5 -1
  12. package/core/loaders/raw_count_loader.js +5 -1
  13. package/core/privacy.d.ts +6 -6
  14. package/core/query/assoc_query.d.ts +1 -0
  15. package/core/query/assoc_query.js +9 -1
  16. package/core/query/custom_clause_query.d.ts +2 -0
  17. package/core/query/custom_clause_query.js +9 -3
  18. package/core/query/custom_query.d.ts +2 -2
  19. package/core/query/custom_query.js +29 -21
  20. package/core/query/query.d.ts +7 -3
  21. package/core/query/query.js +101 -60
  22. package/core/query/shared_assoc_test.d.ts +2 -1
  23. package/core/query/shared_assoc_test.js +24 -45
  24. package/core/query/shared_test.d.ts +5 -1
  25. package/core/query/shared_test.js +354 -301
  26. package/graphql/graphql.js +10 -6
  27. package/graphql/query/shared_edge_connection.js +1 -15
  28. package/imports/index.js +5 -1
  29. package/index.js +5 -1
  30. package/package.json +1 -1
  31. package/schema/field.js +11 -1
  32. package/schema/index.js +5 -1
  33. package/schema/schema.d.ts +1 -0
  34. package/scripts/custom_compiler.js +10 -6
  35. package/scripts/custom_graphql.js +5 -1
  36. package/scripts/read_schema.js +5 -1
  37. package/testutils/db/temp_db.d.ts +6 -5
  38. package/testutils/db/temp_db.js +40 -28
  39. package/testutils/db_mock.js +3 -1
  40. package/testutils/ent-graphql-tests/index.js +8 -1
  41. package/testutils/fake_data/const.d.ts +2 -1
  42. package/testutils/fake_data/const.js +3 -0
  43. package/testutils/fake_data/fake_contact.d.ts +2 -0
  44. package/testutils/fake_data/fake_tag.d.ts +35 -0
  45. package/testutils/fake_data/fake_tag.js +88 -0
  46. package/testutils/fake_data/fake_user.d.ts +2 -0
  47. package/testutils/fake_data/index.js +5 -1
  48. package/testutils/fake_data/internal.d.ts +2 -0
  49. package/testutils/fake_data/internal.js +7 -1
  50. package/testutils/fake_data/tag_query.d.ts +13 -0
  51. package/testutils/fake_data/tag_query.js +43 -0
  52. package/testutils/fake_data/test_helpers.d.ts +8 -2
  53. package/testutils/fake_data/test_helpers.js +21 -7
  54. package/testutils/fake_data/user_query.d.ts +5 -0
  55. package/testutils/fake_data/user_query.js +28 -3
  56. package/testutils/test_edge_global_schema.js +5 -1
  57. package/testutils/write.js +5 -1
  58. package/tsc/ast.js +5 -1
  59. package/tsc/compilerOptions.js +5 -1
  60. package/tsc/move_generated.js +5 -1
  61. package/tsc/transform.js +5 -1
  62. package/tsc/transform_action.js +5 -1
  63. package/tsc/transform_schema.js +5 -1
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.commonTests = void 0;
4
- const db_mock_1 = require("../../testutils/db_mock");
5
4
  const ent_1 = require("../ent");
6
5
  const viewer_1 = require("../viewer");
7
6
  const index_1 = require("../../testutils/fake_data/index");
@@ -12,236 +11,284 @@ const logger_1 = require("../logger");
12
11
  const test_edge_global_schema_1 = require("../../testutils/test_edge_global_schema");
13
12
  const builder_1 = require("../../testutils/builder");
14
13
  const action_1 = require("../../action");
15
- const mock_log_1 = require("../../testutils/mock_log");
16
- class TestQueryFilter {
17
- constructor(filter, newQuery, ents, defaultViewer) {
18
- this.filter = filter;
19
- this.newQuery = newQuery;
20
- this.ents = ents;
21
- this.defaultViewer = defaultViewer;
22
- this.allContacts = [];
23
- this.filteredContacts = [];
24
- // @ts-ignore
25
- const q = this.newQuery(this.defaultViewer);
26
- // TODO sad not generic enough
27
- this.customQuery =
28
- q instanceof index_1.UserToContactsFkeyQuery ||
29
- q instanceof index_1.UserToContactsFkeyQueryDeprecated;
30
- }
31
- async createData() {
32
- [this.user, this.allContacts] = await (0, test_helpers_1.createAllContacts)();
33
- // this.allContacts = this.allContacts.reverse();
34
- this.filteredContacts = this.ents(this.allContacts);
35
- db_mock_1.QueryRecorder.clearQueries();
36
- }
37
- getQuery(viewer) {
38
- return this.filter(this.newQuery(viewer || this.defaultViewer, this.user), this.user, this.allContacts);
39
- }
40
- async testIDs() {
41
- const ids = await this.getQuery().queryIDs();
42
- this.verifyIDs(ids);
43
- }
44
- verifyIDs(ids) {
45
- expect(ids).toEqual(this.filteredContacts.map((contact) => contact.id));
46
- }
47
- // rawCount isn't affected by filters...
48
- async testRawCount(expectedCount) {
49
- const count = await this.getQuery().queryRawCount();
50
- this.verifyRawCount(count, expectedCount);
51
- }
52
- verifyRawCount(count, expectedCount) {
53
- expect(count).toBe(expectedCount ?? test_helpers_1.inputs.length);
54
- }
55
- async testCount(expectedCount) {
56
- const count = await this.getQuery().queryCount();
57
- this.verifyCount(count, expectedCount);
58
- }
59
- verifyCount(count, expectedCount) {
60
- expect(count).toBe(expectedCount ?? this.filteredContacts.length);
61
- }
62
- async testEdges() {
63
- const edges = await this.getQuery().queryEdges();
64
- this.verifyEdges(edges);
14
+ const clause_1 = require("../clause");
15
+ function getWhereClause(query) {
16
+ const idx = query.query.indexOf("WHERE");
17
+ if (idx !== -1) {
18
+ return query.query.substr(idx + 6);
65
19
  }
66
- verifyEdges(edges) {
67
- const q = this.getQuery();
20
+ return null;
21
+ }
22
+ const commonTests = (opts) => {
23
+ (0, logger_1.setLogLevels)(["query", "error"]);
24
+ const ml = opts.ml;
25
+ ml.mock();
26
+ function isCustomQuery(q) {
27
+ if (q.customQuery !== undefined) {
28
+ return q.customQuery;
29
+ }
68
30
  // TODO sad not generic enough
69
- if (this.customQuery) {
70
- (0, test_helpers_1.verifyUserToContactRawData)(this.user, edges, this.filteredContacts);
31
+ return (q instanceof index_1.UserToContactsFkeyQuery ||
32
+ q instanceof index_1.UserToContactsFkeyQueryDeprecated ||
33
+ q instanceof index_1.UserToContactsFkeyQueryAsc);
34
+ }
35
+ class TestQueryFilter {
36
+ constructor(filter, newQuery, ents, defaultViewer) {
37
+ this.filter = filter;
38
+ this.newQuery = newQuery;
39
+ this.ents = ents;
40
+ this.defaultViewer = defaultViewer;
41
+ this.allContacts = [];
42
+ this.filteredContacts = [];
43
+ // @ts-ignore
44
+ const q = this.newQuery(this.defaultViewer);
45
+ this.customQuery = isCustomQuery(q);
71
46
  }
72
- else {
73
- (0, test_helpers_1.verifyUserToContactEdges)(this.user, edges, this.filteredContacts);
47
+ async createData() {
48
+ [this.user, this.allContacts] = await (0, test_helpers_1.createAllContacts)();
49
+ // this.allContacts = this.allContacts.reverse();
50
+ this.filteredContacts = this.ents(this.allContacts);
51
+ ml.clear();
74
52
  }
75
- }
76
- async testEnts(v) {
77
- const ents = await this.getQuery(v || new viewer_1.IDViewer(this.user.id)).queryEnts();
78
- this.verifyEnts(ents);
79
- return ents;
80
- }
81
- async testEntsCache() {
82
- if (!this.customQuery) {
83
- return;
53
+ getQuery(viewer) {
54
+ return this.filter(this.newQuery(viewer || this.defaultViewer, this.user), this.user, this.allContacts);
84
55
  }
85
- (0, logger_1.setLogLevels)(["query", "cache"]);
86
- const ml = new mock_log_1.MockLogs();
87
- ml.mock();
88
- const v = new test_context_1.TestContext(new viewer_1.IDViewer(this.user.id)).getViewer();
89
- const ents = await this.testEnts(v);
90
- expect(ml.logs.length).toBe(1);
91
- expect(ml.logs[0].query).toMatch(/SELECT (.+) FROM /);
92
- await Promise.all(ents.map((ent) => index_1.FakeContact.loadX(v, ent.id)));
93
- expect(ml.logs.length).toBe(this.filteredContacts.length + 1);
94
- for (const log of ml.logs.slice(1)) {
95
- expect(log["ent-cache-hit"]).toBeDefined();
56
+ async testIDs() {
57
+ const ids = await this.getQuery().queryIDs();
58
+ this.verifyIDs(ids);
96
59
  }
97
- ml.restore();
98
- (0, logger_1.clearLogLevels)();
99
- }
100
- async testDataCache() {
101
- if (!this.customQuery) {
102
- return;
60
+ verifyIDs(ids) {
61
+ expect(ids).toEqual(this.filteredContacts.map((contact) => contact.id));
103
62
  }
104
- (0, logger_1.setLogLevels)(["query", "cache"]);
105
- const ml = new mock_log_1.MockLogs();
106
- ml.mock();
107
- const v = new test_context_1.TestContext(new viewer_1.IDViewer(this.user.id)).getViewer();
108
- const ents = await this.testEnts(v);
109
- expect(ml.logs.length).toBe(1);
110
- expect(ml.logs[0].query).toMatch(/SELECT (.+) FROM /);
111
- await Promise.all(ents.map((ent) => index_1.FakeContact.loadRawData(ent.id, v.context)));
112
- expect(ml.logs.length).toBe(this.filteredContacts.length + 1);
113
- for (const log of ml.logs.slice(1)) {
114
- expect(log["dataloader-cache-hit"]).toBeDefined();
63
+ // rawCount isn't affected by filters...
64
+ async testRawCount(expectedCount) {
65
+ const count = await this.getQuery().queryRawCount();
66
+ this.verifyRawCount(count, expectedCount);
115
67
  }
116
- ml.restore();
117
- (0, logger_1.clearLogLevels)();
118
- }
119
- verifyEnts(ents) {
120
- (0, test_helpers_1.verifyUserToContacts)(this.user, ents, this.filteredContacts);
121
- }
122
- async testAll(expectedCount) {
123
- const query = this.getQuery(new viewer_1.IDViewer(this.user.id));
124
- const [edges, count, ids, rawCount, ents] = await Promise.all([
125
- query.queryEdges(),
126
- query.queryCount(),
127
- query.queryIDs(),
128
- query.queryRawCount(),
129
- query.queryEnts(),
130
- ]);
131
- this.verifyCount(count, expectedCount);
132
- this.verifyEdges(edges);
133
- this.verifyIDs(ids);
134
- this.verifyRawCount(rawCount, expectedCount);
135
- this.verifyEnts(ents);
136
- }
137
- }
138
- const preparedVar = /(\$(\d))/g;
139
- const commonTests = (opts) => {
140
- function verifyQuery({ length = 1, numQueries = 1, limit = ent_1.DefaultLimit, disablePaginationBump = false, }) {
141
- if (opts.livePostgresDB || opts.sqlite) {
142
- return;
68
+ verifyRawCount(count, expectedCount) {
69
+ expect(count).toBe(expectedCount ?? test_helpers_1.inputs.length);
70
+ }
71
+ async testCount(expectedCount) {
72
+ const count = await this.getQuery().queryCount();
73
+ this.verifyCount(count, expectedCount);
74
+ }
75
+ verifyCount(count, expectedCount) {
76
+ expect(count).toBe(expectedCount ?? this.filteredContacts.length);
77
+ }
78
+ async testEdges() {
79
+ const edges = await this.getQuery().queryEdges();
80
+ this.verifyEdges(edges);
81
+ }
82
+ verifyEdges(edges) {
83
+ const q = this.getQuery();
84
+ // TODO sad not generic enough
85
+ if (this.customQuery) {
86
+ (0, test_helpers_1.verifyUserToContactRawData)(this.user, edges, this.filteredContacts);
87
+ }
88
+ else {
89
+ (0, test_helpers_1.verifyUserToContactEdges)(this.user, edges, this.filteredContacts);
90
+ }
91
+ }
92
+ async testEnts(v) {
93
+ const ents = await this.getQuery(v || new viewer_1.IDViewer(this.user.id)).queryEnts();
94
+ this.verifyEnts(ents);
95
+ return ents;
96
+ }
97
+ async testEntsCache() {
98
+ if (!this.customQuery) {
99
+ return;
100
+ }
101
+ (0, logger_1.setLogLevels)(["query", "cache"]);
102
+ const v = new test_context_1.TestContext(new viewer_1.IDViewer(this.user.id)).getViewer();
103
+ const ents = await this.testEnts(v);
104
+ expect(ml.logs.length).toBe(1);
105
+ expect(ml.logs[0].query).toMatch(/SELECT (.+) FROM /);
106
+ await Promise.all(ents.map((ent) => index_1.FakeContact.loadX(v, ent.id)));
107
+ expect(ml.logs.length).toBe(this.filteredContacts.length + 1);
108
+ for (const log of ml.logs.slice(1)) {
109
+ expect(log["ent-cache-hit"]).toBeDefined();
110
+ }
111
+ // "restore" back to previous
112
+ (0, logger_1.setLogLevels)("query");
113
+ }
114
+ async testDataCache() {
115
+ if (!this.customQuery) {
116
+ return;
117
+ }
118
+ (0, logger_1.setLogLevels)(["query", "cache"]);
119
+ const v = new test_context_1.TestContext(new viewer_1.IDViewer(this.user.id)).getViewer();
120
+ const ents = await this.testEnts(v);
121
+ expect(ml.logs.length).toBe(1);
122
+ expect(ml.logs[0].query).toMatch(/SELECT (.+) FROM /);
123
+ await Promise.all(ents.map((ent) => index_1.FakeContact.loadRawData(ent.id, v.context)));
124
+ expect(ml.logs.length).toBe(this.filteredContacts.length + 1);
125
+ for (const log of ml.logs.slice(1)) {
126
+ expect(log["dataloader-cache-hit"]).toBeDefined();
127
+ }
128
+ // "restore" back to previous
129
+ (0, logger_1.setLogLevels)("query");
130
+ }
131
+ verifyEnts(ents) {
132
+ (0, test_helpers_1.verifyUserToContacts)(this.user, ents, this.filteredContacts);
133
+ }
134
+ async testAll(expectedCount) {
135
+ const query = this.getQuery(new viewer_1.IDViewer(this.user.id));
136
+ const [edges, count, ids, rawCount, ents] = await Promise.all([
137
+ query.queryEdges(),
138
+ query.queryCount(),
139
+ query.queryIDs(),
140
+ query.queryRawCount(),
141
+ query.queryEnts(),
142
+ ]);
143
+ this.verifyCount(count, expectedCount);
144
+ this.verifyEdges(edges);
145
+ this.verifyIDs(ids);
146
+ this.verifyRawCount(rawCount, expectedCount);
147
+ this.verifyEnts(ents);
143
148
  }
144
- const queries = db_mock_1.QueryRecorder.getCurrentQueries();
145
- expect(queries.length).toBe(length);
149
+ }
150
+ function verifyQuery(filter, { length = 1, numQueries = 1, limit = ent_1.DefaultLimit, disablePaginationBump = false, orderby = opts.orderby, }) {
151
+ const uniqCol = isCustomQuery(filter) ? "id" : "id2";
152
+ expect(ml.logs.length).toBe(length);
146
153
  for (let i = 0; i < numQueries; i++) {
147
- const query = queries[i];
154
+ const whereClause = getWhereClause(ml.logs[i]);
148
155
  let expLimit = disablePaginationBump ? limit : limit + 1;
149
- expect(query.qs?.whereClause, `${i}`).toBe(
156
+ expect(whereClause, `${i}`).toBe(
150
157
  // default limit
151
- `${opts.where} ORDER BY ${opts.sortCol} DESC LIMIT ${expLimit}`);
158
+ `${opts.clause.clause(1)} ORDER BY ${opts.sortCol} ${orderby}, ${uniqCol} ${orderby} LIMIT ${expLimit}`);
152
159
  }
153
160
  }
154
161
  function verifyCountQuery({ length = 1, numQueries = 1 }) {
155
- if (opts.livePostgresDB || opts.sqlite) {
156
- return;
157
- }
158
- const queries = db_mock_1.QueryRecorder.getCurrentQueries();
159
- expect(queries.length).toBe(length);
162
+ expect(ml.logs.length).toBe(length);
160
163
  for (let i = 0; i < numQueries; i++) {
161
- const query = queries[i];
162
- expect(query.qs?.whereClause).toBe(opts.where);
163
- expect(query.qs?.columns).toHaveLength(1);
164
- expect(query.qs?.columns).toStrictEqual(["count(1) as count"]);
164
+ const whereClause = getWhereClause(ml.logs[i]);
165
+ expect(whereClause).toBe(opts.clause.clause(1));
165
166
  }
166
167
  }
167
- function verifyFirstAfterCursorQuery(length = 1) {
168
- if (opts.livePostgresDB || opts.sqlite) {
169
- return;
170
- }
171
- const queries = db_mock_1.QueryRecorder.getCurrentQueries();
172
- expect(queries.length).toBe(length);
173
- const query = queries[0];
174
- const result = [...opts.where.matchAll(preparedVar)];
175
- let parts = opts.where.split(" AND ");
168
+ function verifyFirstAfterCursorQuery(filter, length = 1, limit = 3) {
169
+ // cache showing up in a few because of cross runs...
170
+ expect(ml.logs.length).toBeGreaterThanOrEqual(length);
171
+ const uniqCol = isCustomQuery(filter) ? "id" : "id2";
172
+ let parts = opts.clause.clause(1).split(" AND ");
173
+ const cmp = (0, clause_1.PaginationMultipleColsSubQuery)(opts.sortCol, opts.orderby === "DESC" ? "<" : ">", opts.tableName, uniqCol, "").clause(opts.clause.values().length + 1);
176
174
  if (parts[parts.length - 1] === "deleted_at IS NULL") {
177
175
  parts = parts
178
176
  .slice(0, parts.length - 1)
179
- .concat([
180
- `${opts.sortCol} < $${result.length + 1}`,
181
- "deleted_at IS NULL",
182
- ]);
177
+ .concat([cmp, "deleted_at IS NULL"]);
183
178
  }
184
179
  else {
185
- parts.push(`${opts.sortCol} < $${result.length + 1}`);
180
+ parts.push(cmp);
186
181
  }
187
- expect(query.qs?.whereClause).toBe(`${parts.join(" AND ")} ORDER BY ${opts.sortCol} DESC LIMIT 4`);
182
+ expect(getWhereClause(ml.logs[0])).toBe(`${parts.join(" AND ")} ORDER BY ${opts.sortCol} ${opts.orderby}, ${uniqCol} ${opts.orderby} LIMIT ${limit + 1}`);
188
183
  }
189
- function verifyLastBeforeCursorQuery(length = 1) {
190
- if (opts.livePostgresDB || opts.sqlite) {
191
- return;
184
+ function verifyLastBeforeCursorQuery(filter, length = 1, limit = 3) {
185
+ // cache showing up in a few because of cross runs...
186
+ expect(ml.logs.length).toBeGreaterThanOrEqual(length);
187
+ const uniqCol = isCustomQuery(filter) ? "id" : "id2";
188
+ let op = "";
189
+ let orderby = "";
190
+ if (opts.orderby === "DESC") {
191
+ op = ">";
192
+ orderby = "ASC";
193
+ }
194
+ else {
195
+ op = "<";
196
+ orderby = "DESC";
192
197
  }
193
- const queries = db_mock_1.QueryRecorder.getCurrentQueries();
194
- expect(queries.length).toBe(length);
195
- const query = queries[0];
196
- const result = [...opts.where.matchAll(preparedVar)];
197
- let parts = opts.where.split(" AND ");
198
+ let parts = opts.clause.clause(1).split(" AND ");
199
+ const cmp = (0, clause_1.PaginationMultipleColsSubQuery)(opts.sortCol, op, opts.tableName, uniqCol, "").clause(opts.clause.values().length + 1);
198
200
  if (parts[parts.length - 1] === "deleted_at IS NULL") {
199
201
  parts = parts
200
202
  .slice(0, parts.length - 1)
201
- .concat([
202
- `${opts.sortCol} > $${result.length + 1}`,
203
- "deleted_at IS NULL",
204
- ]);
203
+ .concat([cmp, "deleted_at IS NULL"]);
205
204
  }
206
205
  else {
207
- parts.push(`${opts.sortCol} > $${result.length + 1}`);
206
+ parts.push(cmp);
208
207
  }
209
- expect(query.qs?.whereClause).toBe(
208
+ expect(getWhereClause(ml.logs[0])).toBe(
210
209
  // extra fetched for pagination
211
- `${parts.join(" AND ")} ORDER BY ${opts.sortCol} ASC LIMIT 4`);
210
+ `${parts.join(" AND ")} ORDER BY ${opts.sortCol} ${orderby}, ${uniqCol} ${orderby} LIMIT ${limit + 1}`);
212
211
  }
213
212
  function getViewer() {
214
- // live db, let's do context because we're testing complicated paths
215
- // may be worth breaking this out later
216
- // opts.liveDB no context too...
217
- // maybe this one we just always hit the db
218
- // we don't get value out of testing parse_sql no context....
219
- if (opts.livePostgresDB || opts.sqlite) {
220
- return new test_context_1.TestContext().getViewer();
221
- }
222
- // no context when not live db
223
213
  return new viewer_1.LoggedOutViewer();
224
214
  }
225
215
  function getCursorFrom(contacts, idx) {
226
- // we depend on the fact that the same time is used for the edge and created_at
227
- // based on getContactBuilder
228
- // so regardless of if we're doing assoc or custom queries, we can get the time
229
- // from the created_at field
230
216
  return (0, ent_1.getCursor)({
231
217
  row: contacts[idx],
232
- col: "createdAt",
233
- conv: (t) => {
234
- //sqlite
235
- if (typeof t === "string") {
236
- return Date.parse(t);
237
- }
238
- return t.getTime();
239
- },
240
- // we want the right column to be encoded in the cursor as opposed e.g. time for
241
- // assoc queries, created_at for index/custom queries
242
- cursorKey: opts.sortCol,
218
+ col: "id",
243
219
  });
244
220
  }
221
+ function getVerifyAfterEachCursor(edges, pageLength, user) {
222
+ let query;
223
+ async function verify(i, hasEdge, hasNextPage, cursor) {
224
+ ml.clear();
225
+ query = opts.newQuery(getViewer(), user);
226
+ const newEdges = await query.first(pageLength, cursor).queryEdges();
227
+ const pagination = query.paginationInfo().get(user.id);
228
+ if (hasEdge) {
229
+ expect(newEdges[0], `${i}`).toStrictEqual(edges[i]);
230
+ expect(newEdges.length, `${i}`).toBe(edges.length - i >= pageLength ? pageLength : edges.length - i);
231
+ }
232
+ else {
233
+ expect(newEdges.length, `${i}`).toBe(0);
234
+ }
235
+ if (hasNextPage) {
236
+ expect(pagination?.hasNextPage, `${i}`).toBe(true);
237
+ expect(pagination?.hasPreviousPage, `${i}`).toBe(false);
238
+ }
239
+ else {
240
+ expect(pagination?.hasNextPage, `${i}`).toBe(undefined);
241
+ expect(pagination?.hasNextPage, `${i}`).toBe(undefined);
242
+ }
243
+ if (cursor) {
244
+ verifyFirstAfterCursorQuery(query, 1, pageLength);
245
+ }
246
+ else {
247
+ verifyQuery(query, { orderby: opts.orderby, limit: pageLength });
248
+ }
249
+ }
250
+ function getCursor(edge) {
251
+ return query.getCursor(edge);
252
+ }
253
+ return { verify, getCursor };
254
+ }
255
+ function getVerifyBeforeEachCursor(edges, pageLength, user) {
256
+ let query;
257
+ async function verify(i, hasEdge, hasPreviousPage, cursor) {
258
+ ml.clear();
259
+ query = opts.newQuery(getViewer(), user);
260
+ const newEdges = await query.last(pageLength, cursor).queryEdges();
261
+ const pagination = query.paginationInfo().get(user.id);
262
+ if (hasEdge) {
263
+ expect(newEdges.length, `${i}`).toBe(i >= pageLength ? pageLength : i + 1);
264
+ expect(newEdges[0], `${i}`).toStrictEqual(edges[i]);
265
+ }
266
+ else {
267
+ expect(newEdges.length, `${i}`).toBe(0);
268
+ }
269
+ if (hasPreviousPage) {
270
+ expect(pagination?.hasPreviousPage, `${i}`).toBe(true);
271
+ expect(pagination?.hasNextPage, `${i}`).toBe(false);
272
+ }
273
+ else {
274
+ expect(pagination?.hasPreviousPage, `${i}`).toBe(undefined);
275
+ expect(pagination?.hasNextPage, `${i}`).toBe(undefined);
276
+ }
277
+ if (cursor) {
278
+ verifyLastBeforeCursorQuery(query, 1, pageLength);
279
+ }
280
+ else {
281
+ verifyQuery(query, {
282
+ orderby: opts.orderby === "DESC" ? "ASC" : "DESC",
283
+ limit: pageLength,
284
+ });
285
+ }
286
+ }
287
+ function getCursor(edge) {
288
+ return query.getCursor(edge);
289
+ }
290
+ return { verify, getCursor };
291
+ }
245
292
  if (opts.globalSchema) {
246
293
  (0, ent_1.setGlobalSchema)(test_edge_global_schema_1.testEdgeGlobalSchema);
247
294
  }
@@ -252,16 +299,8 @@ const commonTests = (opts) => {
252
299
  });
253
300
  }
254
301
  beforeAll(async () => {
255
- // want error on by default in tests?
256
- (0, logger_1.setLogLevels)(["error", "warn", "info"]);
257
302
  if (opts.livePostgresDB) {
258
- tdb = await (0, test_helpers_1.setupTempDB)();
259
- return;
260
- }
261
- await (0, test_helpers_1.createEdges)();
262
- });
263
- beforeEach(async () => {
264
- if (opts.livePostgresDB || opts.sqlite) {
303
+ tdb = await (0, test_helpers_1.setupTempDB)(opts.globalSchema);
265
304
  return;
266
305
  }
267
306
  await (0, test_helpers_1.createEdges)();
@@ -278,14 +317,17 @@ const commonTests = (opts) => {
278
317
  }, opts.newQuery, (contacts) => {
279
318
  // nothing to do here
280
319
  // reverse because edges are most recent first
281
- return contacts.reverse();
320
+ if (opts.orderby === "DESC") {
321
+ return contacts.reverse();
322
+ }
323
+ return contacts;
282
324
  }, getViewer());
283
325
  beforeEach(async () => {
284
326
  await filter.createData();
285
327
  });
286
328
  test("ids", async () => {
287
329
  await filter.testIDs();
288
- verifyQuery({});
330
+ verifyQuery(filter, {});
289
331
  });
290
332
  test("rawCount", async () => {
291
333
  await filter.testRawCount();
@@ -293,15 +335,15 @@ const commonTests = (opts) => {
293
335
  });
294
336
  test("count", async () => {
295
337
  await filter.testCount();
296
- verifyQuery({});
338
+ verifyQuery(filter, {});
297
339
  });
298
340
  test("edges", async () => {
299
341
  await filter.testEdges();
300
- verifyQuery({});
342
+ verifyQuery(filter, {});
301
343
  });
302
344
  test("ents", async () => {
303
345
  await filter.testEnts();
304
- verifyQuery({ length: opts.entsLength });
346
+ verifyQuery(filter, { length: opts.entsLength });
305
347
  });
306
348
  test("all", async () => {
307
349
  await filter.testAll();
@@ -330,11 +372,11 @@ const commonTests = (opts) => {
330
372
  await action2.save();
331
373
  }));
332
374
  await action.save();
333
- db_mock_1.QueryRecorder.clearQueries();
375
+ ml.clear();
334
376
  });
335
377
  test("ids", async () => {
336
378
  await filter.testIDs();
337
- verifyQuery({});
379
+ verifyQuery(filter, {});
338
380
  });
339
381
  test("rawCount", async () => {
340
382
  await filter.testRawCount(0);
@@ -342,16 +384,16 @@ const commonTests = (opts) => {
342
384
  });
343
385
  test("count", async () => {
344
386
  await filter.testCount(0);
345
- verifyQuery({});
387
+ verifyQuery(filter, {});
346
388
  });
347
389
  test("edges", async () => {
348
390
  await filter.testEdges();
349
- verifyQuery({});
391
+ verifyQuery(filter, {});
350
392
  });
351
393
  test("ents", async () => {
352
394
  await filter.testEnts();
353
395
  // no ents so no subsequent query. just the edge query
354
- verifyQuery({ length: 1 });
396
+ verifyQuery(filter, { length: 1 });
355
397
  });
356
398
  test("all", async () => {
357
399
  await filter.testAll(0);
@@ -374,14 +416,17 @@ const commonTests = (opts) => {
374
416
  // no filters
375
417
  return q.first(N);
376
418
  }, opts.newQuery, (contacts) => {
377
- return contacts.reverse().slice(0, N);
419
+ if (opts.orderby === "DESC") {
420
+ return contacts.reverse().slice(0, N);
421
+ }
422
+ return contacts.slice(0, N);
378
423
  }, getViewer());
379
424
  beforeEach(async () => {
380
425
  await filter.createData();
381
426
  });
382
427
  test("ids", async () => {
383
428
  await filter.testIDs();
384
- verifyQuery({ limit: 2 });
429
+ verifyQuery(filter, { limit: 2 });
385
430
  });
386
431
  test("rawCount", async () => {
387
432
  await filter.testRawCount();
@@ -389,15 +434,15 @@ const commonTests = (opts) => {
389
434
  });
390
435
  test("count", async () => {
391
436
  await filter.testCount();
392
- verifyQuery({ limit: 2 });
437
+ verifyQuery(filter, { limit: 2 });
393
438
  });
394
439
  test("edges", async () => {
395
440
  await filter.testEdges();
396
- verifyQuery({ limit: 2 });
441
+ verifyQuery(filter, { limit: 2 });
397
442
  });
398
443
  test("ents", async () => {
399
444
  await filter.testEnts();
400
- verifyQuery({ limit: 2, length: opts.entsLength });
445
+ verifyQuery(filter, { limit: 2, length: opts.entsLength });
401
446
  });
402
447
  test("all", async () => {
403
448
  await filter.testAll();
@@ -415,15 +460,23 @@ const commonTests = (opts) => {
415
460
  // no filters
416
461
  return q.last(N);
417
462
  }, opts.newQuery, (contacts) => {
418
- // take the first N and then reverse it to get the last N in the right order
419
- return contacts.slice(0, N).reverse();
463
+ if (opts.orderby === "DESC") {
464
+ return contacts.slice(0, N);
465
+ }
466
+ else {
467
+ return contacts.reverse().slice(0, N);
468
+ }
420
469
  }, getViewer());
470
+ const orderby = opts.orderby === "ASC" ? "DESC" : "ASC";
421
471
  beforeEach(async () => {
422
472
  await filter.createData();
423
473
  });
424
474
  test("ids", async () => {
425
475
  await filter.testIDs();
426
- verifyQuery({ disablePaginationBump: true });
476
+ verifyQuery(filter, {
477
+ orderby,
478
+ limit: N,
479
+ });
427
480
  });
428
481
  test("rawCount", async () => {
429
482
  await filter.testRawCount();
@@ -431,15 +484,19 @@ const commonTests = (opts) => {
431
484
  });
432
485
  test("count", async () => {
433
486
  await filter.testCount();
434
- verifyQuery({ disablePaginationBump: true });
487
+ verifyQuery(filter, { orderby, limit: N });
435
488
  });
436
489
  test("edges", async () => {
437
490
  await filter.testEdges();
438
- verifyQuery({ disablePaginationBump: true });
491
+ verifyQuery(filter, { orderby, limit: N });
439
492
  });
440
493
  test("ents", async () => {
441
494
  await filter.testEnts();
442
- verifyQuery({ disablePaginationBump: true, length: opts.entsLength });
495
+ verifyQuery(filter, {
496
+ orderby,
497
+ limit: N,
498
+ length: opts.entsLength,
499
+ });
443
500
  });
444
501
  test("all", async () => {
445
502
  await filter.testAll();
@@ -457,25 +514,23 @@ const commonTests = (opts) => {
457
514
  const filter = new TestQueryFilter((q, user, contacts) => {
458
515
  return q.first(N, getCursorFrom(contacts, idx));
459
516
  }, opts.newQuery, (contacts) => {
460
- // < check so we shouldn't get that index
461
- return contacts.reverse().slice(idx + 1, idx + N);
462
- }, getViewer());
463
- beforeAll(async () => {
464
- if (opts.livePostgresDB || opts.sqlite) {
465
- await filter.createData();
517
+ if (opts.orderby === "DESC") {
518
+ // < check so we shouldn't get that index
519
+ return contacts.reverse().slice(idx + 1, idx + N);
466
520
  }
467
- });
468
- // TODO do we still need QueryRecorder?
469
- // should just delete this...
470
- beforeEach(async () => {
471
- if (opts.livePostgresDB || opts.sqlite) {
472
- return;
521
+ else {
522
+ return contacts.slice(idx + 1, idx + N);
473
523
  }
524
+ }, getViewer());
525
+ beforeAll(async () => {
474
526
  await filter.createData();
475
527
  });
528
+ beforeEach(() => {
529
+ ml.clear();
530
+ });
476
531
  test("ids", async () => {
477
532
  await filter.testIDs();
478
- verifyFirstAfterCursorQuery();
533
+ verifyFirstAfterCursorQuery(filter);
479
534
  });
480
535
  test("rawCount", async () => {
481
536
  await filter.testRawCount();
@@ -483,15 +538,15 @@ const commonTests = (opts) => {
483
538
  });
484
539
  test("count", async () => {
485
540
  await filter.testCount();
486
- verifyFirstAfterCursorQuery();
541
+ verifyFirstAfterCursorQuery(filter);
487
542
  });
488
543
  test("edges", async () => {
489
544
  await filter.testEdges();
490
- verifyFirstAfterCursorQuery();
545
+ verifyFirstAfterCursorQuery(filter);
491
546
  });
492
547
  test("ents", async () => {
493
548
  await filter.testEnts();
494
- verifyFirstAfterCursorQuery(opts.entsLength);
549
+ verifyFirstAfterCursorQuery(filter, opts.entsLength);
495
550
  });
496
551
  test("all", async () => {
497
552
  await filter.testAll();
@@ -504,36 +559,15 @@ const commonTests = (opts) => {
504
559
  });
505
560
  });
506
561
  test("first. after each cursor", async () => {
507
- let [user, contacts] = await (0, test_helpers_1.createAllContacts)();
508
- contacts = contacts.reverse();
562
+ let [user] = await (0, test_helpers_1.createAllContacts)();
509
563
  const edges = await opts.newQuery(getViewer(), user).queryEdges();
510
- let query;
511
- async function verify(i, hasEdge, hasNextPage, cursor) {
512
- query = opts.newQuery(getViewer(), user);
513
- const newEdges = await query.first(1, cursor).queryEdges();
514
- const pagination = query.paginationInfo().get(user.id);
515
- if (hasEdge) {
516
- expect(newEdges.length, `${i}`).toBe(1);
517
- expect(newEdges[0], `${i}`).toStrictEqual(edges[i]);
518
- }
519
- else {
520
- expect(newEdges.length, `${i}`).toBe(0);
521
- }
522
- if (hasNextPage) {
523
- expect(pagination?.hasNextPage).toBe(true);
524
- expect(pagination?.hasPreviousPage).toBe(false);
525
- }
526
- else {
527
- expect(pagination?.hasNextPage).toBe(undefined);
528
- expect(pagination?.hasNextPage).toBe(undefined);
529
- }
530
- }
564
+ const { verify, getCursor } = getVerifyAfterEachCursor(edges, 1, user);
531
565
  await verify(0, true, true, undefined);
532
- await verify(1, true, true, query.getCursor(edges[0]));
533
- await verify(2, true, true, query.getCursor(edges[1]));
534
- await verify(3, true, true, query.getCursor(edges[2]));
535
- await verify(4, true, false, query.getCursor(edges[3]));
536
- await verify(5, false, false, query.getCursor(edges[4]));
566
+ await verify(1, true, true, getCursor(edges[0]));
567
+ await verify(2, true, true, getCursor(edges[1]));
568
+ await verify(3, true, true, getCursor(edges[2]));
569
+ await verify(4, true, false, getCursor(edges[3]));
570
+ await verify(5, false, false, getCursor(edges[4]));
537
571
  });
538
572
  describe("last. before cursor", () => {
539
573
  const idx = 2;
@@ -542,23 +576,22 @@ const commonTests = (opts) => {
542
576
  return q.last(N, getCursorFrom(contacts, idx));
543
577
  }, opts.newQuery, (contacts) => {
544
578
  // > check so we don't want that index
545
- return contacts.reverse().slice(0, idx).reverse(); // because of order returned
579
+ if (opts.orderby === "DESC") {
580
+ return contacts.reverse().slice(0, idx).reverse(); // because of order returned
581
+ }
582
+ return contacts.slice(0, idx).reverse(); // because of order returned
546
583
  }, getViewer());
547
584
  beforeAll(async () => {
548
585
  if (opts.livePostgresDB || opts.sqlite) {
549
586
  await filter.createData();
550
587
  }
551
588
  });
552
- // same TODO above
553
589
  beforeEach(async () => {
554
- if (opts.livePostgresDB || opts.sqlite) {
555
- return;
556
- }
557
- await filter.createData();
590
+ ml.clear();
558
591
  });
559
592
  test("ids", async () => {
560
593
  await filter.testIDs();
561
- verifyLastBeforeCursorQuery();
594
+ verifyLastBeforeCursorQuery(filter);
562
595
  });
563
596
  test("rawCount", async () => {
564
597
  await filter.testRawCount();
@@ -566,15 +599,15 @@ const commonTests = (opts) => {
566
599
  });
567
600
  test("count", async () => {
568
601
  await filter.testCount();
569
- verifyLastBeforeCursorQuery();
602
+ verifyLastBeforeCursorQuery(filter);
570
603
  });
571
604
  test("edges", async () => {
572
605
  await filter.testEdges();
573
- verifyLastBeforeCursorQuery();
606
+ verifyLastBeforeCursorQuery(filter);
574
607
  });
575
608
  test("ents", async () => {
576
609
  await filter.testEnts();
577
- verifyLastBeforeCursorQuery(opts.entsLength);
610
+ verifyLastBeforeCursorQuery(filter, opts.entsLength);
578
611
  });
579
612
  test("all", async () => {
580
613
  await filter.testAll();
@@ -587,36 +620,56 @@ const commonTests = (opts) => {
587
620
  });
588
621
  });
589
622
  test("last. before each cursor", async () => {
590
- let [user, contacts] = await (0, test_helpers_1.createAllContacts)();
591
- contacts = contacts.reverse();
623
+ let [user] = await (0, test_helpers_1.createAllContacts)();
592
624
  const edges = await opts.newQuery(getViewer(), user).queryEdges();
593
- let query;
594
- async function verify(i, hasEdge, hasPreviousPage, cursor) {
595
- query = opts.newQuery(getViewer(), user);
596
- const newEdges = await query.last(1, cursor).queryEdges();
597
- const pagination = query.paginationInfo().get(user.id);
598
- if (hasEdge) {
599
- expect(newEdges.length, `${i}`).toBe(1);
600
- expect(newEdges[0], `${i}`).toStrictEqual(edges[i]);
601
- }
602
- else {
603
- expect(newEdges.length, `${i}`).toBe(0);
604
- }
605
- if (hasPreviousPage) {
606
- expect(pagination?.hasPreviousPage).toBe(true);
607
- expect(pagination?.hasNextPage).toBe(false);
608
- }
609
- else {
610
- expect(pagination?.hasPreviousPage).toBe(undefined);
611
- expect(pagination?.hasNextPage).toBe(undefined);
612
- }
613
- }
625
+ const { verify, getCursor } = getVerifyBeforeEachCursor(edges, 1, user);
614
626
  await verify(4, true, true, undefined);
615
- await verify(3, true, true, query.getCursor(edges[4]));
616
- await verify(2, true, true, query.getCursor(edges[3]));
617
- await verify(1, true, true, query.getCursor(edges[2]));
618
- await verify(0, true, false, query.getCursor(edges[1]));
619
- await verify(-1, false, false, query.getCursor(edges[0]));
627
+ await verify(3, true, true, getCursor(edges[4]));
628
+ await verify(2, true, true, getCursor(edges[3]));
629
+ await verify(1, true, true, getCursor(edges[2]));
630
+ await verify(0, true, false, getCursor(edges[1]));
631
+ await verify(-1, false, false, getCursor(edges[0]));
632
+ });
633
+ describe("with conflicts", () => {
634
+ let user;
635
+ let allEdges = [];
636
+ beforeEach(async () => {
637
+ const [u, contacts] = await (0, test_helpers_1.createAllContacts)();
638
+ user = u;
639
+ const [_, contacts2] = await (0, test_helpers_1.createAllContacts)({
640
+ user,
641
+ start: contacts[contacts.length - 1].createdAt.getTime() - 100,
642
+ });
643
+ await (0, test_helpers_1.createAllContacts)({
644
+ user,
645
+ start: contacts2[contacts.length - 1].createdAt.getTime() - 100,
646
+ });
647
+ const edges = await opts.newQuery(getViewer(), user).queryEdges();
648
+ // confirm there are duplicates...
649
+ expect(edges[4].created_at).toStrictEqual(edges[5].created_at);
650
+ expect(edges[9].created_at).toStrictEqual(edges[10].created_at);
651
+ allEdges = edges;
652
+ });
653
+ test("first after each cursor", async () => {
654
+ const { verify, getCursor } = getVerifyAfterEachCursor(allEdges, 5, user);
655
+ // regular pagination
656
+ await verify(0, true, true, undefined);
657
+ await verify(5, true, true, getCursor(allEdges[4]));
658
+ await verify(10, true, false, getCursor(allEdges[9]));
659
+ await verify(15, false, false, getCursor(allEdges[14]));
660
+ // one without duplicates work if we were paginating at a different place...
661
+ await verify(6, true, true, getCursor(allEdges[5]));
662
+ await verify(11, true, false, getCursor(allEdges[10]));
663
+ });
664
+ test("last before each cursor", async () => {
665
+ const { verify, getCursor } = getVerifyBeforeEachCursor(allEdges, 5, user);
666
+ await verify(14, true, true, undefined);
667
+ await verify(13, true, true, getCursor(allEdges[14]));
668
+ await verify(8, true, true, getCursor(allEdges[9]));
669
+ await verify(9, true, true, getCursor(allEdges[10]));
670
+ await verify(3, true, false, getCursor(allEdges[4]));
671
+ await verify(4, true, false, getCursor(allEdges[5]));
672
+ });
620
673
  });
621
674
  };
622
675
  exports.commonTests = commonTests;