tantan-typeorm-gs 1.0.0
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.
- package/LICENSE +21 -0
- package/README.md +31 -0
- package/bun.lock +300 -0
- package/docs/authentication.md +118 -0
- package/docs/configuration.md +680 -0
- package/docs/contributing.md +177 -0
- package/docs/crud.md +351 -0
- package/docs/custom-client.md +165 -0
- package/docs/development.md +243 -0
- package/docs/entities.md +502 -0
- package/docs/find-options.md +296 -0
- package/docs/google-cloud.md +101 -0
- package/docs/installation.md +51 -0
- package/docs/integration-testing.md +178 -0
- package/docs/limitations.md +223 -0
- package/docs/pagination.md +384 -0
- package/docs/performance.md +171 -0
- package/docs/security.md +153 -0
- package/docs/sorting.md +0 -0
- package/docs/supported-features.md +310 -0
- package/docs/synchronization.md +203 -0
- package/package.json +43 -0
- package/src/client/index.ts +589 -0
- package/src/core/data-source.ts +47 -0
- package/src/core/driver.ts +290 -0
- package/src/core/error.ts +144 -0
- package/src/core/memory.ts +152 -0
- package/src/core/query/interpreter.ts +949 -0
- package/src/core/query/runner.ts +1297 -0
- package/src/core/query/types.ts +155 -0
- package/src/core/schema-builder.ts +64 -0
- package/src/core/types.ts +113 -0
- package/src/core/utils.ts +13 -0
- package/src/index.ts +3 -0
- package/tantan-typeorm-gs.code-workspace +8 -0
- package/test/base/0001-data-source.test.ts +252 -0
- package/test/base/0002-operator.test.ts +616 -0
- package/test/base/0003-select.test.ts +281 -0
- package/test/base/0004-aggregate.test.ts +213 -0
- package/test/base/0005-transcation.test.ts +1566 -0
- package/test/base/0006-relation.test.ts +1611 -0
- package/test/base/0007-logging.test.ts +182 -0
- package/test/base/0008-soft-delete.test.ts +649 -0
- package/test/google-sheets/0001-client.test.ts +1705 -0
- package/test/google-sheets/0002-worksheet-management.test.ts +408 -0
- package/test/google-sheets/0003-data-source.test.ts +1935 -0
- package/test/google-sheets/0004-transactions.test.ts +952 -0
- package/test/google-sheets/0005-operator.test.ts +1124 -0
- package/test/google-sheets/0006-object-criteria.test.ts +283 -0
- package/test/google-sheets/0007-performance.test.ts +532 -0
- package/test/public-api.test.ts +34 -0
- package/tsconfig.build.json +21 -0
- package/tsconfig.json +34 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
## Find Options
|
|
2
|
+
|
|
3
|
+
`tantan-typeorm-gs` supports TypeORM find options for filtering, ordering, and limiting query results.
|
|
4
|
+
|
|
5
|
+
The options are passed directly to repository methods such as `find()`, `findOne()`, `findAndCount()`, and `count()`.
|
|
6
|
+
|
|
7
|
+
### Where
|
|
8
|
+
|
|
9
|
+
Use `where` to filter rows.
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
const users = await repository.find({
|
|
13
|
+
where: {
|
|
14
|
+
name: "Budi"
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Multiple properties can be specified:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
const users = await repository.find({
|
|
23
|
+
where: {
|
|
24
|
+
name: "Budi",
|
|
25
|
+
email: "budi@example.com"
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The conditions are evaluated against the corresponding worksheet columns.
|
|
31
|
+
|
|
32
|
+
### OR Conditions
|
|
33
|
+
|
|
34
|
+
Multiple `where` objects can be supplied to express OR conditions:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
const users = await repository.find({
|
|
38
|
+
where: [
|
|
39
|
+
{
|
|
40
|
+
name: "Budi"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: "Siti"
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
This returns rows matching either condition.
|
|
50
|
+
|
|
51
|
+
Conceptually:
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
name = 'Budi'
|
|
55
|
+
OR
|
|
56
|
+
name = 'Siti'
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Order
|
|
60
|
+
|
|
61
|
+
Use `order` to sort the result.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
const users = await repository.find({
|
|
65
|
+
order: {
|
|
66
|
+
name: "ASC"
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Descending order:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
const users = await repository.find({
|
|
75
|
+
order: {
|
|
76
|
+
name: "DESC"
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Multiple fields can be specified:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
const users = await repository.find({
|
|
85
|
+
order: {
|
|
86
|
+
name: "ASC",
|
|
87
|
+
email: "DESC"
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The ordering is applied to the returned result rather than changing the underlying worksheet.
|
|
93
|
+
|
|
94
|
+
### Skip
|
|
95
|
+
|
|
96
|
+
Use `skip` to ignore a number of matching rows from the beginning of the result.
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
const users = await repository.find({
|
|
100
|
+
skip: 10
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
For example, with:
|
|
105
|
+
|
|
106
|
+
```text
|
|
107
|
+
skip: 10
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
the first 10 matching rows are omitted from the returned result.
|
|
111
|
+
|
|
112
|
+
### Take
|
|
113
|
+
|
|
114
|
+
Use `take` to limit the number of returned rows.
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
const users = await repository.find({
|
|
118
|
+
take: 10
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
This returns at most 10 rows.
|
|
123
|
+
|
|
124
|
+
### Pagination
|
|
125
|
+
|
|
126
|
+
`skip` and `take` can be combined to implement offset-based pagination:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
const page = 2;
|
|
130
|
+
const pageSize = 10;
|
|
131
|
+
|
|
132
|
+
const users = await repository.find({
|
|
133
|
+
skip: (page - 1) * pageSize,
|
|
134
|
+
take: pageSize
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
For page 2 with a page size of 10:
|
|
139
|
+
|
|
140
|
+
```text
|
|
141
|
+
skip = 10
|
|
142
|
+
take = 10
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The dedicated **Pagination** section provides a more complete example.
|
|
146
|
+
|
|
147
|
+
### Combining Options
|
|
148
|
+
|
|
149
|
+
Find options can be combined:
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
const users = await repository.find({
|
|
153
|
+
where: {
|
|
154
|
+
name: "Budi"
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
order: {
|
|
158
|
+
email: "ASC"
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
skip: 10,
|
|
162
|
+
|
|
163
|
+
take: 10
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The operation can therefore:
|
|
168
|
+
|
|
169
|
+
1. filter matching rows
|
|
170
|
+
2. order the result
|
|
171
|
+
3. skip rows
|
|
172
|
+
4. limit the returned rows
|
|
173
|
+
|
|
174
|
+
### Find One
|
|
175
|
+
|
|
176
|
+
`findOne()` accepts the same style of find options:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
const user = await repository.findOne({
|
|
180
|
+
where: {
|
|
181
|
+
id: 1
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Ordering can also be supplied when selecting a single result:
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
const user = await repository.findOne({
|
|
190
|
+
where: {
|
|
191
|
+
name: "Budi"
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
order: {
|
|
195
|
+
id: "ASC"
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
If no matching entity exists, `findOne()` returns `null`.
|
|
201
|
+
|
|
202
|
+
### Find and Count
|
|
203
|
+
|
|
204
|
+
Use `findAndCount()` when both the result set and the total number of matching rows are required.
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
const [users, total] = await repository.findAndCount({
|
|
208
|
+
where: {
|
|
209
|
+
name: "Budi"
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The result contains:
|
|
215
|
+
|
|
216
|
+
```text
|
|
217
|
+
users
|
|
218
|
+
total
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
`skip` and `take` can be used to limit the returned entities while the count represents the total matching entities.
|
|
222
|
+
|
|
223
|
+
For example:
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
const [users, total] = await repository.findAndCount({
|
|
227
|
+
where: {
|
|
228
|
+
name: "Budi"
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
skip: 10,
|
|
232
|
+
|
|
233
|
+
take: 10
|
|
234
|
+
});
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
This allows the application to implement pagination while still knowing the total number of matching rows.
|
|
238
|
+
|
|
239
|
+
### Count
|
|
240
|
+
|
|
241
|
+
Use `count()` to count matching entities:
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
const total = await repository.count({
|
|
245
|
+
where: {
|
|
246
|
+
name: "Budi"
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
The count is based on the matching rows.
|
|
252
|
+
|
|
253
|
+
`skip` and `take` should not be used when the intention is to obtain the total number of matching entities.
|
|
254
|
+
|
|
255
|
+
### Example
|
|
256
|
+
|
|
257
|
+
A typical paginated search can be written as:
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
const page = 1;
|
|
261
|
+
const pageSize = 20;
|
|
262
|
+
|
|
263
|
+
const [users, total] = await repository.findAndCount({
|
|
264
|
+
where: {
|
|
265
|
+
name: "Budi"
|
|
266
|
+
},
|
|
267
|
+
|
|
268
|
+
order: {
|
|
269
|
+
name: "ASC"
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
skip: (page - 1) * pageSize,
|
|
273
|
+
|
|
274
|
+
take: pageSize
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
console.log("Total:", total);
|
|
278
|
+
console.log("Rows:", users);
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Supported Find Options
|
|
282
|
+
|
|
283
|
+
The currently supported and tested find-option behavior includes:
|
|
284
|
+
|
|
285
|
+
| Option | Purpose |
|
|
286
|
+
| ---------------- | --------------------------------- |
|
|
287
|
+
| `where` | Filter rows |
|
|
288
|
+
| `where: []` | OR conditions |
|
|
289
|
+
| `order` | Sort results |
|
|
290
|
+
| `skip` | Offset results |
|
|
291
|
+
| `take` | Limit results |
|
|
292
|
+
| `findOne()` | Retrieve one matching entity |
|
|
293
|
+
| `findAndCount()` | Retrieve entities and total count |
|
|
294
|
+
| `count()` | Count matching entities |
|
|
295
|
+
|
|
296
|
+
More advanced TypeORM find operators should only be considered supported when explicitly implemented and tested by the driver.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
## Google Cloud Setup
|
|
2
|
+
|
|
3
|
+
`tantan-typeorm-gs` uses the Google Sheets API to read and modify spreadsheet data.
|
|
4
|
+
|
|
5
|
+
The recommended setup for this driver is a **Google Cloud service account** with access to the target Google Spreadsheet.
|
|
6
|
+
|
|
7
|
+
### 1. Create a Google Cloud Project
|
|
8
|
+
|
|
9
|
+
Create or select a project in Google Cloud Console.
|
|
10
|
+
|
|
11
|
+
The project will be used to manage the Google Sheets API and the service account used by the application.
|
|
12
|
+
|
|
13
|
+
### 2. Enable Google Sheets API
|
|
14
|
+
|
|
15
|
+
Open the **API Library** in Google Cloud Console and enable:
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
Google Sheets API
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The API service name is:
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
sheets.googleapis.com
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Google Cloud requires the corresponding API to be enabled before an application can use it.
|
|
28
|
+
|
|
29
|
+
### 3. Create a Service Account
|
|
30
|
+
|
|
31
|
+
Open:
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
IAM & Admin → Service Accounts
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Create a new service account for the application.
|
|
38
|
+
|
|
39
|
+
For example:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
tantan-typeorm-gs
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
A service account represents the application rather than an individual Google user and can be granted access to Google Cloud resources.
|
|
46
|
+
|
|
47
|
+
After creating the service account, note its email address:
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
tantan-typeorm-gs@YOUR_PROJECT_ID.iam.gserviceaccount.com
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 4. Create Service Account Credentials
|
|
54
|
+
|
|
55
|
+
For local development, create a service account key in JSON format from the service account's **Keys** section.
|
|
56
|
+
|
|
57
|
+
The generated credential contains sensitive information, including the private key.
|
|
58
|
+
|
|
59
|
+
**Do not commit the JSON key to Git.**
|
|
60
|
+
|
|
61
|
+
Google notes that user-managed service account keys are long-lived credentials and should be handled carefully. For workloads running on Google Cloud or supported external environments, more secure alternatives such as attached service accounts or Workload Identity Federation should be considered.
|
|
62
|
+
|
|
63
|
+
### 5. Share the Spreadsheet
|
|
64
|
+
|
|
65
|
+
Open the Google Spreadsheet that will be used by the application.
|
|
66
|
+
|
|
67
|
+
Share the spreadsheet with the service account email:
|
|
68
|
+
|
|
69
|
+
```text
|
|
70
|
+
tantan-typeorm-gs@YOUR_PROJECT_ID.iam.gserviceaccount.com
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Grant the service account the required access level.
|
|
74
|
+
|
|
75
|
+
For this driver, the service account must be able to access the spreadsheet and perform the operations required by the application. Google also documents sharing the spreadsheet directly with the service account as part of Sheets API service-account setup.
|
|
76
|
+
|
|
77
|
+
### 6. Get the Spreadsheet ID
|
|
78
|
+
|
|
79
|
+
The spreadsheet ID is part of the Google Sheets URL:
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
https://docs.google.com/spreadsheets/d/SPREADSHEET_ID/edit
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
For example:
|
|
86
|
+
|
|
87
|
+
```text
|
|
88
|
+
https://docs.google.com/spreadsheets/d/1AbCdEfGhIjKlMnOpQrStUvWxYz/edit
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The spreadsheet ID is:
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
1AbCdEfGhIjKlMnOpQrStUvWxYz
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
This value is passed to the driver's configuration.
|
|
98
|
+
|
|
99
|
+
### Next Step
|
|
100
|
+
|
|
101
|
+
After Google Cloud and spreadsheet access have been configured, configure the driver's authentication credentials in the application.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
## Installation
|
|
2
|
+
|
|
3
|
+
### Requirements
|
|
4
|
+
|
|
5
|
+
Before installing `tantan-typeorm-gs`, make sure your project has:
|
|
6
|
+
|
|
7
|
+
- Node.js or Bun
|
|
8
|
+
- TypeScript
|
|
9
|
+
- TypeORM `1.1.x`
|
|
10
|
+
|
|
11
|
+
The driver is distributed as an npm package and uses TypeORM as its ORM layer.
|
|
12
|
+
|
|
13
|
+
### Install
|
|
14
|
+
|
|
15
|
+
Using npm:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install tantan-typeorm-gs typeorm googleapis
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Using Bun:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
bun add tantan-typeorm-gs typeorm googleapis
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### TypeScript
|
|
28
|
+
|
|
29
|
+
The project using the driver should have TypeScript configured.
|
|
30
|
+
|
|
31
|
+
For example:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"compilerOptions": {
|
|
36
|
+
"target": "ES2022",
|
|
37
|
+
"module": "ESNext",
|
|
38
|
+
"moduleResolution": "Bundler"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Verify Installation
|
|
44
|
+
|
|
45
|
+
After installation, the package can be imported from the application:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { createGoogleSheetsDataSource } from "tantan-typeorm-gs";
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The next step is configuring Google Cloud and Google Sheets API access.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
## Integration Testing
|
|
2
|
+
|
|
3
|
+
The project includes integration tests for validating the Google Sheets API client against the real Google Sheets API.
|
|
4
|
+
|
|
5
|
+
Integration tests are different from the unit and driver-level tests that use `Memory`.
|
|
6
|
+
|
|
7
|
+
### Test Layers
|
|
8
|
+
|
|
9
|
+
The test suite can be viewed in several layers:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
Unit / Driver Tests
|
|
13
|
+
↓
|
|
14
|
+
Memory
|
|
15
|
+
↓
|
|
16
|
+
GoogleSheetsDriver
|
|
17
|
+
↓
|
|
18
|
+
Repository / QueryRunner
|
|
19
|
+
|
|
20
|
+
Integration Tests
|
|
21
|
+
↓
|
|
22
|
+
GoogleSheetsApiClient
|
|
23
|
+
↓
|
|
24
|
+
Google Sheets API
|
|
25
|
+
↓
|
|
26
|
+
Real Spreadsheet
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The fake client is used for fast and deterministic tests.
|
|
30
|
+
|
|
31
|
+
The integration tests validate the behavior of the actual Google Sheets API client.
|
|
32
|
+
|
|
33
|
+
### Fake Client Tests
|
|
34
|
+
|
|
35
|
+
Most driver behavior can be tested without network access:
|
|
36
|
+
|
|
37
|
+
```ts id="8r3m1v"
|
|
38
|
+
const client = new Memory({
|
|
39
|
+
users: []
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const dataSource = createGoogleSheetsDataSource({
|
|
43
|
+
type: "google-sheets",
|
|
44
|
+
|
|
45
|
+
client,
|
|
46
|
+
|
|
47
|
+
entities: [User]
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This approach is useful for testing:
|
|
52
|
+
|
|
53
|
+
- repository operations
|
|
54
|
+
- query execution
|
|
55
|
+
- metadata handling
|
|
56
|
+
- lifecycle hooks
|
|
57
|
+
- subscribers
|
|
58
|
+
- generated primary keys
|
|
59
|
+
- error handling
|
|
60
|
+
- pagination and sorting
|
|
61
|
+
- driver behavior
|
|
62
|
+
|
|
63
|
+
These tests should remain fast and deterministic.
|
|
64
|
+
|
|
65
|
+
### Google Sheets API Integration Tests
|
|
66
|
+
|
|
67
|
+
The Google Sheets API client has dedicated integration coverage.
|
|
68
|
+
|
|
69
|
+
The integration tests exercise operations against the actual Google Sheets API, including:
|
|
70
|
+
|
|
71
|
+
- reading rows
|
|
72
|
+
- inserting rows
|
|
73
|
+
- updating rows
|
|
74
|
+
- deleting rows
|
|
75
|
+
- batch-compatible operations
|
|
76
|
+
- handling Google API responses
|
|
77
|
+
|
|
78
|
+
The test file is:
|
|
79
|
+
|
|
80
|
+
```text id="3q7m9x"
|
|
81
|
+
test/google-sheets/0001-client.test.ts
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Required Configuration
|
|
85
|
+
|
|
86
|
+
Integration tests require valid Google Cloud credentials and access to a test spreadsheet.
|
|
87
|
+
|
|
88
|
+
The test environment should provide the required configuration without committing credentials to the repository.
|
|
89
|
+
|
|
90
|
+
For example:
|
|
91
|
+
|
|
92
|
+
```env id="6v2k4p"
|
|
93
|
+
GOOGLE_SHEETS_SPREADSHEET_ID=...
|
|
94
|
+
GOOGLE_SHEETS_CLIENT_EMAIL=...
|
|
95
|
+
GOOGLE_SHEETS_PRIVATE_KEY=...
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The service account must have access to the spreadsheet used by the integration tests.
|
|
99
|
+
|
|
100
|
+
### Keep Integration Data Isolated
|
|
101
|
+
|
|
102
|
+
A dedicated spreadsheet should be used for integration testing whenever possible.
|
|
103
|
+
|
|
104
|
+
The test spreadsheet should not contain production data.
|
|
105
|
+
|
|
106
|
+
Integration tests may modify worksheet contents, so the test environment should be isolated from important spreadsheets.
|
|
107
|
+
|
|
108
|
+
### Running Integration Tests
|
|
109
|
+
|
|
110
|
+
Run the API client integration test directly:
|
|
111
|
+
|
|
112
|
+
```bash id="1m8x5c"
|
|
113
|
+
bun test test/google-sheets/0001-client.test.ts
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The integration test suite currently contains coverage for the Google Sheets API client and batch operations.
|
|
117
|
+
|
|
118
|
+
### Unit Tests vs Integration Tests
|
|
119
|
+
|
|
120
|
+
Use the fake client when the behavior being tested belongs to the driver:
|
|
121
|
+
|
|
122
|
+
```text id="7q3n8v"
|
|
123
|
+
Driver behavior
|
|
124
|
+
↓
|
|
125
|
+
Memory
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Use the real API integration test when validating the Google API client itself:
|
|
129
|
+
|
|
130
|
+
```text id="5c1m7x"
|
|
131
|
+
GoogleSheetsApiClient
|
|
132
|
+
↓
|
|
133
|
+
Google Sheets API
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
This separation prevents network availability or Google API latency from affecting the majority of the test suite.
|
|
137
|
+
|
|
138
|
+
### Recommended Development Workflow
|
|
139
|
+
|
|
140
|
+
During normal development:
|
|
141
|
+
|
|
142
|
+
```bash id="9v4k2m"
|
|
143
|
+
bun test
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
should be used for the regular test suite.
|
|
147
|
+
|
|
148
|
+
When changing the Google Sheets API client, run the dedicated integration tests as well:
|
|
149
|
+
|
|
150
|
+
```bash id="2x7p5n"
|
|
151
|
+
bun test test/google-sheets/0001-client.test.ts
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Performance tests can be run separately:
|
|
155
|
+
|
|
156
|
+
```bash id="6k3m8q"
|
|
157
|
+
bun test test/google-sheets/0007-performance.test.ts
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Security
|
|
161
|
+
|
|
162
|
+
Never commit integration-test credentials to source control.
|
|
163
|
+
|
|
164
|
+
Use environment variables or the project's secure CI/CD secret mechanism.
|
|
165
|
+
|
|
166
|
+
Integration-test spreadsheets should also be treated as test infrastructure and should not contain sensitive production information.
|
|
167
|
+
|
|
168
|
+
### Summary
|
|
169
|
+
|
|
170
|
+
The project intentionally separates testing responsibilities:
|
|
171
|
+
|
|
172
|
+
| Test type | Client | Purpose |
|
|
173
|
+
| ----------------------- | ----------------------- | --------------------------------- |
|
|
174
|
+
| Driver/repository tests | `Memory` | Fast driver behavior testing |
|
|
175
|
+
| API integration tests | `GoogleSheetsApiClient` | Real Google Sheets API validation |
|
|
176
|
+
| Performance tests | `Memory` | Driver regression baseline |
|
|
177
|
+
|
|
178
|
+
This separation keeps the normal test suite fast while still providing coverage against the real Google Sheets API.
|