Skip to content

Commit 3c3b63b

Browse files
committed
Generate some tests
1 parent c0366fb commit 3c3b63b

File tree

16 files changed

+1785
-668
lines changed

16 files changed

+1785
-668
lines changed

package-lock.json

Lines changed: 753 additions & 68 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.nyc_output
22
dist
33
coverage
4+
mongo-php-library

packages/mql-typescript/out/schema.ts

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type * as bson from 'bson';
2+
import { FilterOperators } from 'mongodb';
23

3-
// from Node.js driver
4-
type Condition<T> = AlternativeType<T>; // misses FilterOperators
4+
type Condition<T> = AlternativeType<T> | FilterOperators<T>;
55
type AlternativeType<T> = T extends ReadonlyArray<infer U>
66
? T | RegExpOrString<U>
77
: RegExpOrString<T>;
@@ -15,6 +15,7 @@ type RecordWithStaticFields<T extends Record<string, any>, TValue> = T & {
1515

1616
// TBD: Nested fields
1717
type AFieldPath<S, Type> = KeysOfAType<S, Type> & string;
18+
type FieldExpression<T> = { [k: string]: FieldPath<T> };
1819
export namespace AccumulatorOperators {
1920
/**
2021
* A type describing the `$accumulator` operator.
@@ -2884,26 +2885,40 @@ export namespace ExpressionOperators {
28842885
* Returns a subset of an array.
28852886
* @see {@link https://www.mongodb.com/docs/manual/reference/operator/aggregation/slice/}
28862887
*/
2887-
$slice: [
2888-
/**
2889-
* Any valid expression as long as it resolves to an array.
2890-
*/
2891-
expression: ResolvesToArray<S>,
2892-
2893-
/**
2894-
* Any valid expression as long as it resolves to an integer.
2895-
* If positive, $slice determines the starting position from the start of the array. If position is greater than the number of elements, the $slice returns an empty array.
2896-
* If negative, $slice determines the starting position from the end of the array. If the absolute value of the <position> is greater than the number of elements, the starting position is the start of the array.
2897-
*/
2898-
position?: ResolvesToInt<S>,
2899-
2900-
/**
2901-
* Any valid expression as long as it resolves to an integer. If position is specified, n must resolve to a positive integer.
2902-
* If positive, $slice returns up to the first n elements in the array. If the position is specified, $slice returns the first n elements starting from the position.
2903-
* If negative, $slice returns up to the last n elements in the array. n cannot resolve to a negative number if <position> is specified.
2904-
*/
2905-
n: ResolvesToInt<S>
2906-
];
2888+
$slice:
2889+
| [
2890+
/**
2891+
* Any valid expression as long as it resolves to an array.
2892+
*/
2893+
expression: ResolvesToArray<S>,
2894+
2895+
/**
2896+
* Any valid expression as long as it resolves to an integer. If position is specified, n must resolve to a positive integer.
2897+
* If positive, $slice returns up to the first n elements in the array. If the position is specified, $slice returns the first n elements starting from the position.
2898+
* If negative, $slice returns up to the last n elements in the array. n cannot resolve to a negative number if <position> is specified.
2899+
*/
2900+
n: ResolvesToInt<S>
2901+
]
2902+
| [
2903+
/**
2904+
* Any valid expression as long as it resolves to an array.
2905+
*/
2906+
expression: ResolvesToArray<S>,
2907+
2908+
/**
2909+
* Any valid expression as long as it resolves to an integer.
2910+
* If positive, $slice determines the starting position from the start of the array. If position is greater than the number of elements, the $slice returns an empty array.
2911+
* If negative, $slice determines the starting position from the end of the array. If the absolute value of the <position> is greater than the number of elements, the starting position is the start of the array.
2912+
*/
2913+
position: ResolvesToInt<S>,
2914+
2915+
/**
2916+
* Any valid expression as long as it resolves to an integer. If position is specified, n must resolve to a positive integer.
2917+
* If positive, $slice returns up to the first n elements in the array. If the position is specified, $slice returns the first n elements starting from the position.
2918+
* If negative, $slice returns up to the last n elements in the array. n cannot resolve to a negative number if <position> is specified.
2919+
*/
2920+
n: ResolvesToInt<S>
2921+
];
29072922
}
29082923

29092924
/**
@@ -5304,7 +5319,12 @@ export type TimeUnit =
53045319
export type OutCollection = unknown;
53055320
export type WhenMatched = string;
53065321
export type WhenNotMatched = string;
5307-
export type Expression<S> = C_expression<S> | FieldPath<S> | BsonPrimitive;
5322+
export type Expression<S> =
5323+
| C_expression<S>
5324+
| FieldPath<S>
5325+
| BsonPrimitive
5326+
| FieldExpression<S>
5327+
| FieldPath<S>[];
53085328
export type Stage<S> =
53095329
| C_stage<S>
53105330
| StageOperators.$addFields<S>

packages/mql-typescript/package.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@
3030
"types": "./dist/index.d.ts",
3131
"scripts": {
3232
"extract-metaschema": "json-refs resolve mongo-php-library/generator/config/schema.json | json-schema-to-zod -n Operator -o src/metaschema.ts && npm run prettier -- --write src/metaschema.ts",
33-
"prestart": "npm run extract-metaschema",
34-
"start": "ts-node src/index.ts > out/schema.ts && npm run prettier -- --write out/schema.ts",
33+
"pregenerate-schema": "npm run extract-metaschema",
34+
"generate-schema": "ts-node src/index.ts schema && npm run prettier -- --write out/schema.ts",
35+
"generate-tests": "ts-node src/index.ts tests && npm run prettier -- --write tests/**/*.ts",
3536
"bootstrap": "npm run compile",
3637
"prepublishOnly": "npm run compile",
3738
"compile": "tsc -p tsconfig.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs",
@@ -55,6 +56,7 @@
5556
"@mongodb-js/tsconfig-devtools": "^1.0.2",
5657
"@types/chai": "^5.2.0",
5758
"@types/js-yaml": "^4.0.9",
59+
"@types/jsdom": "^21.1.7",
5860
"@types/mocha": "^10.0.10",
5961
"@types/node": "^22.13.10",
6062
"@types/sinon-chai": "^4.0.0",
@@ -74,6 +76,11 @@
7476
"dependencies": {
7577
"bson": "^6.10.3",
7678
"js-yaml": "^4.1.0",
79+
"jsdom": "^26.0.0",
80+
"json5": "^2.2.3",
81+
"mongodb": "^6.15.0",
82+
"mongodb-schema": "^12.5.2",
83+
"yargs": "^17.7.2",
7784
"zod": "^3.24.2"
7885
}
7986
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import { createWriteStream } from 'fs';
2+
import { StringWriter } from './utils';
3+
import path from 'path';
4+
import * as fs from 'fs/promises';
5+
import * as yaml from 'js-yaml';
6+
import * as bson from 'bson';
7+
8+
export type YamlFiles = ReturnType<GeneratorBase['listSourceYAMLFiles']>;
9+
10+
export abstract class GeneratorBase {
11+
private outputBuffer: StringWriter | undefined;
12+
private outputStream?: NodeJS.WritableStream;
13+
14+
private static loadOptions: yaml.LoadOptions = {
15+
schema: yaml.DEFAULT_SCHEMA.extend([
16+
new yaml.Type('!bson_utcdatetime', {
17+
kind: 'scalar',
18+
construct(data) {
19+
return new Date(data);
20+
},
21+
}),
22+
new yaml.Type('!bson_objectId', {
23+
kind: 'scalar',
24+
construct(data) {
25+
return bson.ObjectId.createFromHexString(data);
26+
},
27+
}),
28+
new yaml.Type('!bson_uuid', {
29+
kind: 'scalar',
30+
construct(data) {
31+
return bson.UUID.createFromHexString(data);
32+
},
33+
}),
34+
new yaml.Type('!bson_regex', {
35+
kind: 'scalar',
36+
construct(data) {
37+
return new bson.BSONRegExp(data);
38+
},
39+
}),
40+
new yaml.Type('!bson_regex', {
41+
kind: 'sequence',
42+
construct([data, flags]) {
43+
return new bson.BSONRegExp(data, flags);
44+
},
45+
}),
46+
new yaml.Type('!bson_binary', {
47+
kind: 'scalar',
48+
construct([data]) {
49+
return bson.Binary.createFromBase64(data);
50+
},
51+
}),
52+
new yaml.Type('!bson_decimal128', {
53+
kind: 'scalar',
54+
construct([data]) {
55+
return bson.Decimal128.fromString(data);
56+
},
57+
}),
58+
]),
59+
};
60+
61+
private async *listCategories(): AsyncIterable<{
62+
category: string;
63+
folder: string;
64+
}> {
65+
const configDir = path.join(
66+
__dirname,
67+
'..',
68+
'mongo-php-library',
69+
'generator',
70+
'config'
71+
);
72+
73+
for await (const folder of await fs.readdir(configDir, {
74+
withFileTypes: true,
75+
})) {
76+
if (folder.isDirectory()) {
77+
yield {
78+
category: folder.name,
79+
folder: path.join(folder.parentPath, folder.name),
80+
};
81+
}
82+
}
83+
}
84+
85+
private async *listSourceYAMLFiles(): AsyncIterable<{
86+
category: string;
87+
operators: () => AsyncIterable<{ yaml: unknown }>;
88+
}> {
89+
for await (const { category, folder } of this.listCategories()) {
90+
yield {
91+
category,
92+
operators: async function* () {
93+
for await (const file of await fs.readdir(folder, {
94+
withFileTypes: true,
95+
})) {
96+
if (file.isFile() && file.name.endsWith('.yaml')) {
97+
const filePath = path.join(file.parentPath, file.name);
98+
const content = await fs.readFile(filePath, 'utf8');
99+
const parsed = yaml.load(content, GeneratorBase.loadOptions);
100+
yield { yaml: parsed };
101+
}
102+
}
103+
},
104+
};
105+
}
106+
}
107+
108+
protected emitToFile(filePath: string): void {
109+
this.outputStream = createWriteStream(filePath, { encoding: 'utf8' });
110+
}
111+
112+
protected emit(str: string): void {
113+
(this.outputBuffer ?? this.outputStream ?? process.stdout).write(str);
114+
}
115+
116+
protected toComment(str?: string, docsUrl?: string): string {
117+
if (!str) {
118+
return '';
119+
}
120+
121+
return [
122+
'',
123+
'',
124+
'/**',
125+
...str
126+
.replace(/\*\//g, '*//*')
127+
.split('\n')
128+
.map((l) => l.trim())
129+
.filter((l) => l)
130+
.map((l) => ` * ${l}`),
131+
...(docsUrl ? [` * @see {@link ${docsUrl}}`] : []),
132+
' */',
133+
'',
134+
].join('\n');
135+
}
136+
137+
protected emitComment(str: string, docsUrl?: string): void {
138+
this.emit(this.toComment(str, docsUrl));
139+
}
140+
141+
protected getOutputOf(fn: () => void): string {
142+
this.outputBuffer = new StringWriter();
143+
fn();
144+
const output = this.outputBuffer.toString();
145+
this.outputBuffer = undefined;
146+
return output;
147+
}
148+
149+
protected abstract generateImpl(iterable: YamlFiles): Promise<void>;
150+
151+
public generate(): Promise<void> {
152+
const files = this.listSourceYAMLFiles();
153+
return this.generateImpl(files);
154+
}
155+
}

packages/mql-typescript/src/index.spec.ts

Whitespace-only changes.

0 commit comments

Comments
 (0)