Skip to content

Commit ae925a8

Browse files
authored
Minimal integration test for search completition. (#8034)
1 parent 8d438cb commit ae925a8

File tree

4 files changed

+146
-46
lines changed

4 files changed

+146
-46
lines changed

app/lib/fake/backend/fake_pub_worker.dart

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import 'dart:math';
99
import 'package:_pub_shared/dartdoc/dartdoc_page.dart';
1010
import 'package:_pub_shared/data/package_api.dart';
1111
import 'package:_pub_shared/data/task_payload.dart';
12+
import 'package:_pub_shared/worker/docker_utils.dart';
1213
import 'package:clock/clock.dart';
14+
import 'package:gcloud/service_scope.dart';
1315
import 'package:http/http.dart';
1416
import 'package:http_parser/http_parser.dart';
1517
import 'package:indexed_blob/indexed_blob.dart';
@@ -18,6 +20,7 @@ import 'package:pana/pana.dart';
1820
import 'package:path/path.dart' as p;
1921
import 'package:pub_dev/fake/backend/fake_pana_runner.dart';
2022
import 'package:pub_dev/frontend/handlers/pubapi.client.dart';
23+
import 'package:pub_dev/frontend/static_files.dart';
2124
import 'package:pub_dev/scorecard/backend.dart';
2225
import 'package:pub_dev/service/async_queue/async_queue.dart';
2326
import 'package:pub_dev/shared/utils.dart';
@@ -65,13 +68,31 @@ Future<void> processTasksLocallyWithPubWorker() async {
6568
]);
6669
}
6770

71+
/// Process analysis tasks locally, using either:
72+
/// - `fake`: the semi-randomized fake analysis that is fast
73+
/// - `local`: running pkg/pub_worker using the same context as the app
74+
/// - `worker`: running pkg/pub_worker using the dockerized container
75+
Future<void> processTaskFakeLocalOrWorker(String analysis) async {
76+
if (analysis == 'none') {
77+
return;
78+
} else if (analysis == 'local') {
79+
await _analyzeLocal();
80+
} else if (analysis == 'worker') {
81+
await _analyzeWorker();
82+
} else if (analysis == 'fake') {
83+
await processTasksWithFakePanaAndDartdoc();
84+
} else {
85+
throw ArgumentError('Unknown analysis: `$analysis`');
86+
}
87+
}
88+
6889
/// Updates the task status for all packages and imitates
6990
/// pub_worker using fake pana and dartdoc results.
7091
Future<void> processTasksWithFakePanaAndDartdoc() async {
71-
await taskBackend.backfillAndProcessAllPackages(_processPayload);
92+
await taskBackend.backfillAndProcessAllPackages(_fakeAnalysis);
7293
}
7394

74-
Future<void> _processPayload(Payload payload) async {
95+
Future<void> _fakeAnalysis(Payload payload) async {
7596
for (final v in payload.versions) {
7697
final client = httpClientWithAuthorization(
7798
tokenProvider: () async => v.token,
@@ -154,10 +175,40 @@ Future<void> _processPayload(Payload payload) async {
154175
}
155176
}
156177

178+
Future<void> _analyzeLocal() async {
179+
await fork(() async {
180+
await taskBackend.backfillAndProcessAllPackages((Payload payload) async {
181+
final arguments = [json.encode(payload.toJson())];
182+
final pr = await Process.run(
183+
Platform.resolvedExecutable,
184+
['run', 'pub_worker', ...arguments],
185+
workingDirectory: p.join(resolveAppDir(), '..', 'pkg', 'pub_worker'),
186+
);
187+
if (pr.exitCode != 0) {
188+
throw Exception('Unexpected status code: ${pr.exitCode} ${pr.stdout}');
189+
}
190+
});
191+
});
192+
}
193+
194+
Future<void> _analyzeWorker() async {
195+
await buildDockerImage();
196+
await fork(() async {
197+
await taskBackend.backfillAndProcessAllPackages((Payload payload) async {
198+
final p = await startDockerAnalysis(payload);
199+
final exitCode = await p.exitCode;
200+
if (exitCode != 0) {
201+
throw Exception(
202+
'Failed to analyze ${payload.package} with exitCode $exitCode');
203+
}
204+
});
205+
});
206+
}
207+
157208
Future<void> fakeCloudComputeInstanceRunner(FakeCloudInstance instance) async {
158209
final payload = Payload.fromJson(
159210
json.decode(instance.arguments.first) as Map<String, dynamic>);
160-
await _processPayload(payload);
211+
await _fakeAnalysis(payload);
161212
}
162213

163214
Map<String, String> _fakeDartdocFiles(

app/lib/fake/server/fake_server_entrypoint.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,8 @@ Future<shelf.Response> _testProfile(shelf.Request rq) async {
176176
profile: profile,
177177
source: ImportSource.autoGenerated(),
178178
);
179-
await processTasksWithFakePanaAndDartdoc();
179+
final analysis = (map['analysis'] as String?) ?? 'fake';
180+
await processTaskFakeLocalOrWorker(analysis);
180181
return shelf.Response.ok('{}');
181182
}
182183

app/lib/fake/tool/init_data_file.dart

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,15 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
import 'dart:convert';
65
import 'dart:io';
76

8-
import 'package:_pub_shared/data/task_payload.dart';
9-
import 'package:_pub_shared/worker/docker_utils.dart';
107
import 'package:args/command_runner.dart';
11-
import 'package:gcloud/service_scope.dart';
128
import 'package:logging/logging.dart';
139
import 'package:path/path.dart' as p;
1410
import 'package:pub_dev/fake/backend/fake_pub_worker.dart';
1511

1612
import 'package:pub_dev/frontend/static_files.dart';
1713
import 'package:pub_dev/service/services.dart';
18-
import 'package:pub_dev/task/backend.dart';
1914
import 'package:pub_dev/tool/test_profile/import_source.dart';
2015
import 'package:pub_dev/tool/test_profile/importer.dart';
2116
import 'package:pub_dev/tool/test_profile/models.dart';
@@ -90,44 +85,8 @@ class FakeInitDataFileCommand extends Command {
9085
: ImportSource.autoGenerated(),
9186
);
9287

93-
if (analysis == 'local') {
94-
await _analyzeLocal();
95-
} else if (analysis == 'worker') {
96-
await _analyzeWorker();
97-
} else if (analysis == 'fake') {
98-
await processTasksWithFakePanaAndDartdoc();
99-
}
88+
await processTaskFakeLocalOrWorker(analysis);
10089
});
10190
await state.save(dataFile);
10291
}
10392
}
104-
105-
Future<void> _analyzeLocal() async {
106-
await fork(() async {
107-
await taskBackend.backfillAndProcessAllPackages((Payload payload) async {
108-
final arguments = [json.encode(payload.toJson())];
109-
final pr = await Process.run(
110-
Platform.resolvedExecutable,
111-
['run', 'pub_worker', ...arguments],
112-
workingDirectory: p.join(resolveAppDir(), '..', 'pkg', 'pub_worker'),
113-
);
114-
if (pr.exitCode != 0) {
115-
throw Exception('Unexpected status code: ${pr.exitCode} ${pr.stdout}');
116-
}
117-
});
118-
});
119-
}
120-
121-
Future<void> _analyzeWorker() async {
122-
await buildDockerImage();
123-
await fork(() async {
124-
await taskBackend.backfillAndProcessAllPackages((Payload payload) async {
125-
final p = await startDockerAnalysis(payload);
126-
final exitCode = await p.exitCode;
127-
if (exitCode != 0) {
128-
throw Exception(
129-
'Failed to analyze ${payload.package} with exitCode $exitCode');
130-
}
131-
});
132-
});
133-
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:convert';
6+
7+
import 'package:http/http.dart' as http;
8+
import 'package:pub_integration/src/fake_test_context_provider.dart';
9+
import 'package:pub_integration/src/test_browser.dart';
10+
import 'package:puppeteer/puppeteer.dart';
11+
import 'package:test/test.dart';
12+
13+
void main() {
14+
group('search completition', () {
15+
late final TestContextProvider fakeTestScenario;
16+
final httpClient = http.Client();
17+
18+
setUpAll(() async {
19+
fakeTestScenario = await TestContextProvider.start();
20+
});
21+
22+
tearDownAll(() async {
23+
await fakeTestScenario.close();
24+
httpClient.close();
25+
});
26+
27+
test('bulk tests', () async {
28+
final origin = fakeTestScenario.pubHostedUrl;
29+
// init server data
30+
//
31+
// The test profile import uses a fake analysis by default, which
32+
// assigns tags with a pseudorandom process (based on the hash of the
33+
// package's name and sometimes the version), with a few hardcoded
34+
// patterns, e.g. `flutter_*` packages will get `sdk:flutter` tag assigned.
35+
//
36+
// This imports 100 packages with these semi-random tags, and adding and
37+
// removing filters works because of the number of packages and their
38+
// tags are kind of random.
39+
await httpClient.post(Uri.parse('$origin/fake-test-profile'),
40+
body: json.encode({
41+
'testProfile': {
42+
'defaultUser': 'admin@pub.dev',
43+
'packages': [
44+
{
45+
'name': 'oxygen',
46+
'publisher': 'example.com',
47+
},
48+
],
49+
},
50+
'analysis': 'local',
51+
}));
52+
53+
final user = await fakeTestScenario.createAnonymousTestUser();
54+
55+
await user.withBrowserPage((page) async {
56+
await page.gotoOrigin('/experimental?search-completion=1');
57+
58+
await page.gotoOrigin('/');
59+
await page.keyboard.type('is:un');
60+
await Future.delayed(Duration(milliseconds: 200));
61+
await page.keyboard.press(Key.enter);
62+
await Future.delayed(Duration(milliseconds: 200));
63+
await page.keyboard.press(Key.enter);
64+
await page.waitForNavigation();
65+
66+
// TODO: try to fix form submission to trim whitespaces
67+
expect(page.url, '$origin/packages?q=is%3Aunlisted+');
68+
});
69+
70+
await user.withBrowserPage((page) async {
71+
await page.gotoOrigin('/experimental?search-completion=1');
72+
73+
await page.gotoOrigin('/packages?q=abc');
74+
await page.focus('input[name="q"]');
75+
// go to the end of the input field and start typing
76+
await page.keyboard.press(Key.arrowDown);
77+
await page.keyboard.type(' -sdk:fl');
78+
await Future.delayed(Duration(milliseconds: 200));
79+
await page.keyboard.press(Key.enter);
80+
await Future.delayed(Duration(milliseconds: 200));
81+
await page.keyboard.press(Key.enter);
82+
await page.waitForNavigation();
83+
84+
// TODO: try to fix form submission to trim whitespaces
85+
expect(page.url, '$origin/packages?q=abc+-sdk%3Aflutter+');
86+
});
87+
});
88+
}, timeout: Timeout.factor(testTimeoutFactor));
89+
}

0 commit comments

Comments
 (0)