blob: b2e9d3ffd53dcd7dff87b49d39e2e7f4f7c2946a [file] [log] [blame]
// Copyright 2019 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:cocoon_service/src/model/appengine/commit.dart';
import 'package:cocoon_service/src/model/appengine/task.dart';
import 'package:cocoon_service/src/model/luci/push_message.dart';
import 'package:cocoon_service/src/request_handling/exceptions.dart';
import 'package:cocoon_service/src/service/datastore.dart';
import 'package:gcloud/db.dart';
import 'package:test/test.dart';
import '../src/datastore/fake_config.dart';
import '../src/utilities/entity_generators.dart';
void main() {
group('Task', () {
test('byAttempts comparator', () {
final List<Task> tasks = <Task>[
generateTask(1, attempts: 5),
generateTask(2, attempts: 9),
generateTask(3, attempts: 3),
];
tasks.sort(Task.byAttempts);
expect(tasks.map<int>((Task task) => task.attempts!), <int>[3, 5, 9]);
});
test('disallows illegal status', () {
expect(() => generateTask(1, status: 'unknown'), throwsArgumentError);
expect(() => generateTask(1)..status = 'unknown', throwsArgumentError);
});
group('updateFromBuild', () {
test('updates if buildNumber is null', () {
final DateTime created = DateTime.utc(2022, 1, 11, 1, 1);
final DateTime started = DateTime.utc(2022, 1, 11, 1, 2);
final DateTime completed = DateTime.utc(2022, 1, 11, 1, 3);
final Build build = generatePushMessageBuild(
1,
createdTimestamp: created,
startedTimestamp: started,
completedTimestamp: completed,
);
final Task task = generateTask(1);
expect(task.status, Task.statusNew);
expect(task.buildNumberList, isNull);
expect(task.buildNumber, isNull);
expect(task.endTimestamp, 0);
expect(task.createTimestamp, 0);
expect(task.startTimestamp, 0);
task.updateFromBuild(build);
expect(task.status, Task.statusSucceeded);
expect(task.buildNumber, 1);
expect(task.buildNumberList, '1');
expect(task.createTimestamp, created.millisecondsSinceEpoch);
expect(task.startTimestamp, started.millisecondsSinceEpoch);
expect(task.endTimestamp, completed.millisecondsSinceEpoch);
});
test('defaults timestamps to 0', () {
final Build build = generatePushMessageBuild(1);
final Task task = generateTask(1);
expect(task.endTimestamp, 0);
expect(task.createTimestamp, 0);
expect(task.startTimestamp, 0);
task.updateFromBuild(build);
expect(task.endTimestamp, 0);
expect(task.createTimestamp, 0);
expect(task.startTimestamp, 0);
});
test('updates if buildNumber is prior to pushMessage', () {
final Build build = generatePushMessageBuild(
1,
buildNumber: 2,
status: Status.started,
);
final Task task = generateTask(
1,
buildNumber: 1,
status: Task.statusSucceeded,
);
expect(task.buildNumberList, '1');
expect(task.status, Task.statusSucceeded);
task.updateFromBuild(build);
expect(task.buildNumber, 2);
expect(task.buildNumberList, '1,2');
expect(task.status, Task.statusInProgress);
});
test('does not duplicate build numbers on multiple messages', () {
final Build build = generatePushMessageBuild(
1,
status: Status.started,
);
final Task task = generateTask(
1,
buildNumber: 1,
status: Task.statusSucceeded,
);
expect(task.buildNumber, 1);
expect(task.buildNumberList, '1');
expect(task.status, Task.statusSucceeded);
task.updateFromBuild(build);
expect(task.buildNumber, 1);
expect(task.buildNumberList, '1');
expect(task.status, Task.statusSucceeded);
});
test('does not update status if older status', () {
final Build build = generatePushMessageBuild(
1,
status: Status.started,
);
final Task task = generateTask(
1,
buildNumber: 1,
status: Task.statusSucceeded,
);
expect(task.buildNumber, 1);
expect(task.status, Task.statusSucceeded);
task.updateFromBuild(build);
expect(task.buildNumber, 1);
expect(task.status, Task.statusSucceeded);
});
test('does not update if build is older than task', () {
final Build build = generatePushMessageBuild(
1,
status: Status.completed,
result: Result.success,
);
final Task task = generateTask(
1,
buildNumber: 2,
status: Task.statusNew,
);
expect(task.buildNumber, 2);
expect(task.status, Task.statusNew);
task.updateFromBuild(build);
expect(task.buildNumber, 2);
expect(task.status, Task.statusNew);
});
test('handles cancelled build', () {
final Build build = generatePushMessageBuild(
1,
status: Status.completed,
result: Result.canceled,
);
final Task task = generateTask(
1,
buildNumber: 1,
status: Task.statusNew,
);
expect(task.status, Task.statusNew);
task.updateFromBuild(build);
expect(task.status, Task.statusCancelled);
});
});
});
// TODO(chillers): There is a bug where `dart test` does not work in offline mode.
// Need to file issue and get traces.
group('Task.fromDatastore', () {
late FakeConfig config;
late Commit commit;
late Task expectedTask;
setUp(() {
config = FakeConfig();
commit = generateCommit(1);
expectedTask = generateTask(1, parent: commit);
config.db.values[commit.key] = commit;
config.db.values[expectedTask.key] = expectedTask;
});
test('look up by id', () async {
final Task task = await Task.fromDatastore(
datastore: DatastoreService(config.db, 5),
commitKey: commit.key,
id: '${expectedTask.id}',
);
expect(task, expectedTask);
});
test('look up by id fails if cannot be found', () async {
expect(
Task.fromDatastore(
datastore: DatastoreService(config.db, 5),
commitKey: commit.key,
id: '12345',
),
throwsA(isA<KeyNotFoundException>()),
);
});
test('look up by name', () async {
final Task task = await Task.fromDatastore(
datastore: DatastoreService(config.db, 5),
commitKey: commit.key,
name: expectedTask.name,
);
expect(task, expectedTask);
});
test('look up by name fails if cannot be found', () async {
try {
await Task.fromDatastore(
datastore: DatastoreService(config.db, 5),
commitKey: commit.key,
name: 'Linux not_found',
);
} catch (e) {
expect(e, isA<InternalServerError>());
expect(
e.toString(),
equals(
'HTTP 500: Expected to find 1 task for Linux not_found, but found 0',
),
);
}
});
test('look up by name fails if multiple Tasks with the same name are found', () async {
final DatastoreService datastore = DatastoreService(config.db, 5);
final String taskName = expectedTask.name!;
final Task duplicatedTask = generateTask(2, parent: commit, name: taskName);
config.db.values[duplicatedTask.key] = duplicatedTask;
try {
await Task.fromDatastore(
datastore: datastore,
commitKey: commit.key,
name: taskName,
);
} catch (e) {
expect(e, isA<InternalServerError>());
expect(
e.toString(),
equals(
'HTTP 500: Expected to find 1 task for $taskName, but found 2',
),
);
}
});
});
}