@@ -147,6 +147,76 @@ void main() {
147147 });
148148 });
149149
150+ group('package uploader invite', () {
151+ setupTestsWithAdminTokenIssues((client) => client.adminInvokeAction(
152+ 'package-invite-uploader',
153+ AdminInvokeActionArguments(
154+ arguments: {'package': 'oxygen', 'email': '
[email protected] '})));
155+
156+ testWithProfile('invite + accept', fn: () async {
157+ final adminClient = createPubApiClient(authToken: siteAdminToken);
158+ final adminOutput = await adminClient.adminInvokeAction(
159+ 'package-invite-uploader',
160+ AdminInvokeActionArguments(
161+ arguments: {'package': 'oxygen', 'email': '
[email protected] '}));
162+
163+ expect(adminOutput.output, {
164+ 'message': 'Invited user',
165+ 'package': 'oxygen',
166+ 'emailSent': true,
167+ 168+ });
169+
170+ final email = fakeEmailSender.sentMessages.first;
171+ expect(email.subject, 'You have a new invitation to confirm on pub.dev');
172+
173+ final page = await auditBackend.listRecordsForPackage('oxygen');
174+ final r = page.records
175+ .firstWhere((e) => e.kind == AuditLogRecordKind.uploaderInvited);
176+ expect(r.summary,
177+ '`
[email protected] ` invited `
[email protected] ` to be an uploader for package `oxygen`.');
178+
179+ late String consentId;
180+ await withFakeAuthRequestContext(
181+ 182+ () async {
183+ final authenticatedUser = await requireAuthenticatedWebUser();
184+ final user = authenticatedUser.user;
185+ final consentRow = await dbService.query<Consent>().run().single;
186+ final consent =
187+ await consentBackend.getConsent(consentRow.consentId, user);
188+ expect(consent.descriptionHtml, contains('/packages/oxygen'));
189+ expect(consent.descriptionHtml,
190+ contains('perform administrative actions'));
191+ consentId = consentRow.consentId;
192+ },
193+ );
194+
195+ final acceptingClient =
196+ await createFakeAuthPubApiClient(email: '
[email protected] ');
197+ final rs = await acceptingClient.resolveConsent(
198+ consentId, account_api.ConsentResult(granted: true));
199+ expect(rs.granted, true);
200+
201+ final page2 = await auditBackend.listRecordsForPackage('oxygen');
202+ final r2 = page2.records.firstWhere(
203+ (e) => e.kind == AuditLogRecordKind.uploaderInviteAccepted);
204+ expect(r2.summary,
205+ '`
[email protected] ` accepted uploader invite for package `oxygen`.');
206+
207+ final uploaders =
208+ (await packageBackend.lookupPackage('oxygen'))!.uploaders;
209+ expect(uploaders!, hasLength(2));
210+ expect(
211+ await Future.wait(uploaders.map((uploader) async =>
212+ (await accountBackend.lookupUserById(uploader))!.email)),
213+ {
214+ 215+ 216+ });
217+ });
218+ });
219+
150220 group('create and delete publisher', () {
151221 testWithProfile('publisher has packages', fn: () async {
152222 final p1 = await publisherBackend.lookupPublisher('example.com');
0 commit comments