appsok/test/jenkins_artifact_session_test.dart

433 lines
13 KiB
Dart

import 'dart:convert';
import 'dart:io';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
import 'package:appsok/src/models/jenkins_build.dart';
import 'package:appsok/src/services/artifact_staging_service.dart';
import 'package:appsok/src/services/jenkins_artifact_session.dart';
import 'package:appsok/src/services/jenkins_client.dart';
import 'package:appsok/src/services/token_store.dart';
void main() {
const credentials = JenkinsCredentials(username: 'user', apiToken: 'token');
const baseUrl = 'https://jenkins.example';
setUp(() {
FlutterSecureStorage.setMockInitialValues({});
});
test('restore validates stored session with current user api', () async {
FlutterSecureStorage.setMockInitialValues({
'jenkins.baseUrl': baseUrl,
'jenkins.username': credentials.username,
'jenkins.apiToken': credentials.apiToken,
});
Uri? capturedUri;
Map<String, String>? capturedHeaders;
final session = JenkinsArtifactSession(
store: TokenStore(),
client: JenkinsClient(
client: MockClient((request) async {
capturedUri = request.url;
capturedHeaders = request.headers;
if (request.url.path.endsWith('whoAmI/api/json')) {
return http.Response(jsonEncode({'id': 'user'}), 200);
}
return http.Response('unexpected', 400);
}),
),
stager: ArtifactStagingService(),
);
final restored = await session.restore();
expect(restored, isTrue);
expect(capturedUri?.path, '/whoAmI/api/json');
expect(
capturedHeaders?['Authorization'],
'Basic ${base64Encode(utf8.encode('user:token'))}',
);
});
test(
'loadJobs delegates to JenkinsClient with stored base url and credentials',
() async {
FlutterSecureStorage.setMockInitialValues({
'jenkins.baseUrl': baseUrl,
'jenkins.username': credentials.username,
'jenkins.apiToken': credentials.apiToken,
});
Uri? jobsUri;
final session = JenkinsArtifactSession(
store: TokenStore(),
client: JenkinsClient(
client: MockClient((request) async {
if (request.url.path.endsWith('whoAmI/api/json')) {
return http.Response(jsonEncode({'id': 'user'}), 200);
}
if (request.url.path.endsWith('api/json')) {
jobsUri = request.url;
return http.Response(jsonEncode({'jobs': []}), 200);
}
return http.Response('unexpected', 400);
}),
),
stager: ArtifactStagingService(),
);
await session.restore();
await session.loadJobs();
expect(jobsUri, isNotNull);
expect(jobsUri?.path, '/api/json');
expect(jobsUri?.host, 'jenkins.example');
},
);
test(
'downloadArtifact builds Jenkins artifact URL from build url and relative path',
() async {
FlutterSecureStorage.setMockInitialValues({
'jenkins.baseUrl': baseUrl,
'jenkins.username': credentials.username,
'jenkins.apiToken': credentials.apiToken,
});
Uri? artifactUri;
final session = JenkinsArtifactSession(
store: TokenStore(),
client: JenkinsClient(
client: MockClient((request) async {
if (request.url.path.endsWith('whoAmI/api/json')) {
return http.Response(jsonEncode({'id': 'user'}), 200);
}
if (request.url.path.endsWith('artifact/file.apk')) {
artifactUri = request.url;
return http.Response.bytes([], 200);
}
if (request.url.path.endsWith('api/json')) {
return http.Response(jsonEncode({'jobs': []}), 200);
}
return http.Response('unexpected', 400);
}),
),
stager: ArtifactStagingService(),
);
await session.restore();
final task = session.downloadArtifact(
JenkinsBuild(
number: 1,
jobName: 'app',
url: Uri.parse('$baseUrl/job/app/1/'),
startedAt: DateTime(2026),
result: 'SUCCESS',
artifacts: const [
BuildArtifact(
fileName: 'app.apk',
relativePath: 'artifact/file.apk',
),
],
),
const BuildArtifact(
fileName: 'file.apk',
relativePath: 'artifact/file.apk',
),
);
await task.events.toList();
expect(artifactUri, isNotNull);
expect(artifactUri?.path, '/job/app/1/artifact/artifact/file.apk');
},
);
test('returns unavailable state when session config is missing', () async {
final session = JenkinsArtifactSession(
store: TokenStore(),
client: JenkinsClient(
client: MockClient((_) async => http.Response('', 200)),
),
stager: ArtifactStagingService(),
);
final restored = await session.restore();
expect(restored, isFalse);
expect(session.isRestored, isFalse);
});
test('clear removes stored session and in-memory restored state', () async {
FlutterSecureStorage.setMockInitialValues({
'jenkins.baseUrl': baseUrl,
'jenkins.username': credentials.username,
'jenkins.apiToken': credentials.apiToken,
});
final store = TokenStore();
final session = JenkinsArtifactSession(
store: store,
client: JenkinsClient(
client: MockClient((request) async {
if (request.url.path.endsWith('whoAmI/api/json')) {
return http.Response(jsonEncode({'id': 'user'}), 200);
}
return http.Response('unexpected', 400);
}),
),
stager: ArtifactStagingService(),
);
expect(await session.restore(), isTrue);
expect(session.isRestored, isTrue);
await session.clear();
expect(session.isRestored, isFalse);
expect(await store.readSession(), isNull);
});
test('restoreDetailed returns missing when config is missing', () async {
final session = JenkinsArtifactSession(
store: TokenStore(),
client: JenkinsClient(
client: MockClient((_) async => http.Response('', 200)),
),
stager: ArtifactStagingService(),
);
final result = await session.restoreDetailed();
expect(result.kind, JenkinsSessionRestoreKind.missing);
expect(result.isRestored, isFalse);
});
test(
'restoreDetailed returns restored after current user validation',
() async {
FlutterSecureStorage.setMockInitialValues({
'jenkins.baseUrl': baseUrl,
'jenkins.username': credentials.username,
'jenkins.apiToken': credentials.apiToken,
});
final session = JenkinsArtifactSession(
store: TokenStore(),
client: JenkinsClient(
client: MockClient((request) async {
if (request.url.path.endsWith('whoAmI/api/json')) {
return http.Response(jsonEncode({'id': 'user'}), 200);
}
return http.Response('unexpected', 400);
}),
),
stager: ArtifactStagingService(),
);
final result = await session.restoreDetailed();
expect(result.kind, JenkinsSessionRestoreKind.restored);
expect(result.isRestored, isTrue);
expect(session.isRestored, isTrue);
},
);
test(
'restoreDetailed returns reauthRequired and clears session on 401',
() async {
FlutterSecureStorage.setMockInitialValues({
'jenkins.baseUrl': baseUrl,
'jenkins.username': credentials.username,
'jenkins.apiToken': credentials.apiToken,
});
final store = TokenStore();
final session = JenkinsArtifactSession(
store: store,
client: JenkinsClient(
client: MockClient((_) async => http.Response('Unauthorized', 401)),
),
stager: ArtifactStagingService(),
);
final result = await session.restoreDetailed();
expect(result.kind, JenkinsSessionRestoreKind.reauthRequired);
expect(result.isRestored, isFalse);
expect(session.isRestored, isFalse);
expect(await store.readSession(), isNull);
},
);
test(
'restoreDetailed returns reauthRequired and clears session on 403',
() async {
FlutterSecureStorage.setMockInitialValues({
'jenkins.baseUrl': baseUrl,
'jenkins.username': credentials.username,
'jenkins.apiToken': credentials.apiToken,
});
final store = TokenStore();
final session = JenkinsArtifactSession(
store: store,
client: JenkinsClient(
client: MockClient((_) async => http.Response('Forbidden', 403)),
),
stager: ArtifactStagingService(),
);
final result = await session.restoreDetailed();
expect(result.kind, JenkinsSessionRestoreKind.reauthRequired);
expect(result.isRestored, isFalse);
expect(session.isRestored, isFalse);
expect(await store.readSession(), isNull);
},
);
test(
'restoreDetailed returns reauthRequired and clears session on anonymous whoAmI',
() async {
FlutterSecureStorage.setMockInitialValues({
'jenkins.baseUrl': baseUrl,
'jenkins.username': credentials.username,
'jenkins.apiToken': credentials.apiToken,
});
final store = TokenStore();
final session = JenkinsArtifactSession(
store: store,
client: JenkinsClient(
client: MockClient(
(_) async => http.Response(
jsonEncode({
'anonymous': true,
'id': 'anonymous',
'fullName': 'anonymous',
}),
200,
),
),
),
stager: ArtifactStagingService(),
);
final result = await session.restoreDetailed();
expect(result.kind, JenkinsSessionRestoreKind.reauthRequired);
expect(result.isRestored, isFalse);
expect(session.isRestored, isFalse);
expect(await store.readSession(), isNull);
},
);
test(
'restoreDetailed returns networkFailure and keeps session on IOException',
() async {
FlutterSecureStorage.setMockInitialValues({
'jenkins.baseUrl': baseUrl,
'jenkins.username': credentials.username,
'jenkins.apiToken': credentials.apiToken,
});
final store = TokenStore();
final session = JenkinsArtifactSession(
store: store,
client: JenkinsClient(
client: MockClient(
(_) async => throw const SocketException('offline'),
),
),
stager: ArtifactStagingService(),
);
final result = await session.restoreDetailed();
expect(result.kind, JenkinsSessionRestoreKind.networkFailure);
expect(result.isRestored, isFalse);
expect(session.isRestored, isFalse);
expect(await store.readSession(), isNotNull);
},
);
test(
'restoreDetailed returns serverFailure and keeps session on non-auth JenkinsClientException',
() async {
FlutterSecureStorage.setMockInitialValues({
'jenkins.baseUrl': baseUrl,
'jenkins.username': credentials.username,
'jenkins.apiToken': credentials.apiToken,
});
final store = TokenStore();
final session = JenkinsArtifactSession(
store: store,
client: JenkinsClient(
client: MockClient(
(_) async => http.Response('Internal Server Error', 500),
),
),
stager: ArtifactStagingService(),
);
final result = await session.restoreDetailed();
expect(result.kind, JenkinsSessionRestoreKind.serverFailure);
expect(result.isRestored, isFalse);
expect(session.isRestored, isFalse);
expect(await store.readSession(), isNotNull);
},
);
test('restore bool wrapper remains true only for restored', () async {
FlutterSecureStorage.setMockInitialValues({
'jenkins.baseUrl': baseUrl,
'jenkins.username': credentials.username,
'jenkins.apiToken': credentials.apiToken,
});
final session = JenkinsArtifactSession(
store: TokenStore(),
client: JenkinsClient(
client: MockClient((request) async {
if (request.url.path.endsWith('whoAmI/api/json')) {
return http.Response(jsonEncode({'id': 'user'}), 200);
}
return http.Response('unexpected', 400);
}),
),
stager: ArtifactStagingService(),
);
expect(await session.restore(), isTrue);
FlutterSecureStorage.setMockInitialValues({});
final sessionMissing = JenkinsArtifactSession(
store: TokenStore(),
client: JenkinsClient(
client: MockClient((_) async => http.Response('', 200)),
),
stager: ArtifactStagingService(),
);
expect(await sessionMissing.restore(), isFalse);
});
}