feat(shorebird_code_push_protocol): add release analysis API surface (#3744)
This commit is contained in:
@@ -142,6 +142,7 @@ words:
|
||||
- timebase # Mach timebase (mach_timebase_info)
|
||||
- udevadm # From .github/workflows/e2e.yaml
|
||||
- udid # Unique Device Identifier
|
||||
- unanalyzed
|
||||
- unawaited
|
||||
- unmockable
|
||||
- unobfuscated
|
||||
|
||||
@@ -43,6 +43,7 @@ export 'package:shorebird_code_push_protocol/src/models/app.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/app_collaborator_role.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/app_metadata.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/channel.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/latest_release.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/organization.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/organization_membership.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/organization_type.dart';
|
||||
@@ -50,9 +51,11 @@ export 'package:shorebird_code_push_protocol/src/models/organization_user.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/patch.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/patch_artifact.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/patch_check_metadata.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/pending_release.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/private_user.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/public_user.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/release.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/release_analysis.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/release_artifact.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/release_patch.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/release_platform.dart';
|
||||
|
||||
+3
-6
@@ -75,12 +75,9 @@ class PatchCheckRequest {
|
||||
/// unique per app. Optional for backward compatibility.
|
||||
final String? clientId;
|
||||
|
||||
/// The number of the patch currently running on the device, if any.
|
||||
///
|
||||
/// Supersedes [patchNumber] for newer updater clients. Unlike
|
||||
/// [patchNumber], this does not affect the server's response;
|
||||
/// [patchNumber] is retained for compatibility with legacy clients that
|
||||
/// rely on the server's short-circuit path.
|
||||
/// The patch number currently running on the device, if any.
|
||||
/// Supersedes `patch_number` for newer clients; unlike
|
||||
/// `patch_number`, this does not affect the server's response.
|
||||
final int? currentPatchNumber;
|
||||
|
||||
/// Converts a [PatchCheckRequest] to a `Map<String, dynamic>`.
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:shorebird_code_push_protocol/model_helpers.dart';
|
||||
import 'package:shorebird_code_push_protocol/src/models/latest_release.dart';
|
||||
import 'package:shorebird_code_push_protocol/src/models/pending_release.dart';
|
||||
import 'package:shorebird_code_push_protocol/src/models/release_platform.dart';
|
||||
|
||||
/// {@template app_metadata}
|
||||
/// A single app which contains zero or more releases.
|
||||
@@ -14,6 +17,10 @@ class AppMetadata {
|
||||
required this.updatedAt,
|
||||
this.latestReleaseVersion,
|
||||
this.latestPatchNumber,
|
||||
this.platforms,
|
||||
this.latestReleases,
|
||||
this.pendingReleases,
|
||||
this.iconUrl,
|
||||
});
|
||||
|
||||
/// Converts a `Map<String, dynamic>` to an [AppMetadata].
|
||||
@@ -28,6 +35,23 @@ class AppMetadata {
|
||||
latestPatchNumber: json['latest_patch_number'] as int?,
|
||||
createdAt: DateTime.parse(json['created_at'] as String),
|
||||
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||
platforms: (json['platforms'] as List?)
|
||||
?.map<ReleasePlatform>((e) => ReleasePlatform.fromJson(e as String))
|
||||
.toList(),
|
||||
latestReleases: (json['latest_releases'] as Map<String, dynamic>?)?.map(
|
||||
(key, value) => MapEntry(
|
||||
ReleasePlatform.fromJson(key),
|
||||
LatestRelease.fromJson(value as Map<String, dynamic>),
|
||||
),
|
||||
),
|
||||
pendingReleases: (json['pending_releases'] as Map<String, dynamic>?)
|
||||
?.map(
|
||||
(key, value) => MapEntry(
|
||||
ReleasePlatform.fromJson(key),
|
||||
PendingRelease.fromJson(value as Map<String, dynamic>),
|
||||
),
|
||||
),
|
||||
iconUrl: json['icon_url'] as String?,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -59,6 +83,38 @@ class AppMetadata {
|
||||
/// The date and time the app was last updated.
|
||||
final DateTime updatedAt;
|
||||
|
||||
/// Every platform the app has shipped to (i.e. has at least one
|
||||
/// release artifact for). Independent of `latest_releases`:
|
||||
/// an app can list a platform here even if no release on that
|
||||
/// platform has been analyzed yet.
|
||||
final List<ReleasePlatform>? platforms;
|
||||
|
||||
/// The latest analyzed release per platform. A platform is
|
||||
/// omitted when no release for that platform has been analyzed
|
||||
/// yet. When the latest release for a platform is not yet
|
||||
/// analyzed but a previous one is, the previous release is
|
||||
/// returned here and `pending_releases.{platform}` identifies
|
||||
/// the unanalyzed newer release.
|
||||
final Map<ReleasePlatform, LatestRelease>? latestReleases;
|
||||
|
||||
/// The newest unanalyzed release per platform, whenever one
|
||||
/// exists. A platform is omitted when its most recent release
|
||||
/// has already been analyzed. Independent of `latest_releases`:
|
||||
/// both can be present (a newer release than the analyzed one
|
||||
/// is being processed) or only `pending_releases` can be
|
||||
/// present (no release on the platform has been analyzed yet).
|
||||
final Map<ReleasePlatform, PendingRelease>? pendingReleases;
|
||||
|
||||
/// Server-emitted URL for the app's launcher icon, sourced from
|
||||
/// the most recent analyzed iOS release (or Android, when iOS
|
||||
/// has no analyzed release with an icon). Requires the same
|
||||
/// auth as the rest of the apps API. The URL embeds the picked
|
||||
/// release's id as a `v` query parameter so it can be cached
|
||||
/// indefinitely; when a different release becomes the icon
|
||||
/// source the URL changes and clients re-fetch. Omitted when
|
||||
/// no analyzed release has an icon.
|
||||
final String? iconUrl;
|
||||
|
||||
/// Converts an [AppMetadata] to a `Map<String, dynamic>`.
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
@@ -68,6 +124,14 @@ class AppMetadata {
|
||||
'latest_patch_number': latestPatchNumber,
|
||||
'created_at': createdAt.toIso8601String(),
|
||||
'updated_at': updatedAt.toIso8601String(),
|
||||
'platforms': platforms?.map((e) => e.toJson()).toList(),
|
||||
'latest_releases': latestReleases?.map(
|
||||
(key, value) => MapEntry(key.toJson(), value.toJson()),
|
||||
),
|
||||
'pending_releases': pendingReleases?.map(
|
||||
(key, value) => MapEntry(key.toJson(), value.toJson()),
|
||||
),
|
||||
'icon_url': iconUrl,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -79,6 +143,10 @@ class AppMetadata {
|
||||
latestPatchNumber,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
listHash(platforms),
|
||||
mapHash(latestReleases),
|
||||
mapHash(pendingReleases),
|
||||
iconUrl,
|
||||
]);
|
||||
|
||||
@override
|
||||
@@ -90,6 +158,10 @@ class AppMetadata {
|
||||
latestReleaseVersion == other.latestReleaseVersion &&
|
||||
latestPatchNumber == other.latestPatchNumber &&
|
||||
createdAt == other.createdAt &&
|
||||
updatedAt == other.updatedAt;
|
||||
updatedAt == other.updatedAt &&
|
||||
listsEqual(platforms, other.platforms) &&
|
||||
mapsEqual(latestReleases, other.latestReleases) &&
|
||||
mapsEqual(pendingReleases, other.pendingReleases) &&
|
||||
iconUrl == other.iconUrl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:shorebird_code_push_protocol/model_helpers.dart';
|
||||
import 'package:shorebird_code_push_protocol/src/models/release_analysis.dart';
|
||||
import 'package:shorebird_code_push_protocol/src/models/release_status.dart';
|
||||
|
||||
/// {@template latest_release}
|
||||
/// Per-platform projection of an analyzed release as surfaced by
|
||||
/// `AppMetadata.latest_releases`. Each entry corresponds to the
|
||||
/// most recent analyzed release on the keying platform, so
|
||||
/// `analysis` is always populated and `status` is the single
|
||||
/// per-platform status (rather than the cross-platform
|
||||
/// `platform_statuses` map carried by `Release`).
|
||||
/// {@endtemplate}
|
||||
@immutable
|
||||
class LatestRelease {
|
||||
/// {@macro latest_release}
|
||||
const LatestRelease({
|
||||
required this.id,
|
||||
required this.version,
|
||||
required this.flutterRevision,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
required this.status,
|
||||
required this.analysis,
|
||||
this.flutterVersion,
|
||||
this.notes,
|
||||
});
|
||||
|
||||
/// Converts a `Map<String, dynamic>` to a [LatestRelease].
|
||||
factory LatestRelease.fromJson(Map<String, dynamic> json) {
|
||||
return parseFromJson(
|
||||
'LatestRelease',
|
||||
json,
|
||||
() => LatestRelease(
|
||||
id: json['id'] as int,
|
||||
version: json['version'] as String,
|
||||
flutterRevision: json['flutter_revision'] as String,
|
||||
flutterVersion: json['flutter_version'] as String?,
|
||||
createdAt: DateTime.parse(json['created_at'] as String),
|
||||
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||
status: ReleaseStatus.fromJson(json['status'] as String),
|
||||
notes: json['notes'] as String?,
|
||||
analysis: ReleaseAnalysis.fromJson(
|
||||
json['analysis'] as Map<String, dynamic>,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Convenience to create a nullable type from a nullable json object.
|
||||
/// Useful when parsing optional fields.
|
||||
static LatestRelease? maybeFromJson(Map<String, dynamic>? json) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
return LatestRelease.fromJson(json);
|
||||
}
|
||||
|
||||
/// The ID of the release.
|
||||
final int id;
|
||||
|
||||
/// The version of the release.
|
||||
final String version;
|
||||
|
||||
/// The Flutter revision used to create the release.
|
||||
final String flutterRevision;
|
||||
|
||||
/// The Flutter version used to create the release. Optional
|
||||
/// because it was added later; older releases do not have it.
|
||||
final String? flutterVersion;
|
||||
|
||||
/// The date and time the release was created.
|
||||
final DateTime createdAt;
|
||||
|
||||
/// The date and time the release was last updated.
|
||||
final DateTime updatedAt;
|
||||
|
||||
/// The status of a release.
|
||||
final ReleaseStatus status;
|
||||
|
||||
/// Freeform notes associated with the release, if any.
|
||||
final String? notes;
|
||||
|
||||
/// Analyzer-extracted metadata for a release artifact on a single
|
||||
/// platform.
|
||||
final ReleaseAnalysis analysis;
|
||||
|
||||
/// Converts a [LatestRelease] to a `Map<String, dynamic>`.
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'version': version,
|
||||
'flutter_revision': flutterRevision,
|
||||
'flutter_version': flutterVersion,
|
||||
'created_at': createdAt.toIso8601String(),
|
||||
'updated_at': updatedAt.toIso8601String(),
|
||||
'status': status.toJson(),
|
||||
'notes': notes,
|
||||
'analysis': analysis.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hashAll([
|
||||
id,
|
||||
version,
|
||||
flutterRevision,
|
||||
flutterVersion,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
status,
|
||||
notes,
|
||||
analysis,
|
||||
]);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other is LatestRelease &&
|
||||
id == other.id &&
|
||||
version == other.version &&
|
||||
flutterRevision == other.flutterRevision &&
|
||||
flutterVersion == other.flutterVersion &&
|
||||
createdAt == other.createdAt &&
|
||||
updatedAt == other.updatedAt &&
|
||||
status == other.status &&
|
||||
notes == other.notes &&
|
||||
analysis == other.analysis;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:shorebird_code_push_protocol/model_helpers.dart';
|
||||
|
||||
/// {@template pending_release}
|
||||
/// A newer release that has been created but not yet analyzed.
|
||||
/// Surfaced alongside the most recent analyzed release so clients
|
||||
/// can show an "analyzing…" indicator without losing the stable
|
||||
/// icon and metadata.
|
||||
/// {@endtemplate}
|
||||
@immutable
|
||||
class PendingRelease {
|
||||
/// {@macro pending_release}
|
||||
const PendingRelease({
|
||||
required this.id,
|
||||
required this.version,
|
||||
required this.createdAt,
|
||||
});
|
||||
|
||||
/// Converts a `Map<String, dynamic>` to a [PendingRelease].
|
||||
factory PendingRelease.fromJson(Map<String, dynamic> json) {
|
||||
return parseFromJson(
|
||||
'PendingRelease',
|
||||
json,
|
||||
() => PendingRelease(
|
||||
id: json['id'] as int,
|
||||
version: json['version'] as String,
|
||||
createdAt: DateTime.parse(json['created_at'] as String),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Convenience to create a nullable type from a nullable json object.
|
||||
/// Useful when parsing optional fields.
|
||||
static PendingRelease? maybeFromJson(Map<String, dynamic>? json) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
return PendingRelease.fromJson(json);
|
||||
}
|
||||
|
||||
/// The ID of the pending release.
|
||||
final int id;
|
||||
|
||||
/// The version of the pending release.
|
||||
final String version;
|
||||
|
||||
/// The date and time the pending release was created.
|
||||
final DateTime createdAt;
|
||||
|
||||
/// Converts a [PendingRelease] to a `Map<String, dynamic>`.
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'version': version,
|
||||
'created_at': createdAt.toIso8601String(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hashAll([
|
||||
id,
|
||||
version,
|
||||
createdAt,
|
||||
]);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other is PendingRelease &&
|
||||
id == other.id &&
|
||||
version == other.version &&
|
||||
createdAt == other.createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:shorebird_code_push_protocol/model_helpers.dart';
|
||||
|
||||
/// {@template release_analysis}
|
||||
/// Analyzer-extracted metadata for a release artifact on a single
|
||||
/// platform.
|
||||
/// {@endtemplate}
|
||||
@immutable
|
||||
class ReleaseAnalysis {
|
||||
/// {@macro release_analysis}
|
||||
const ReleaseAnalysis({
|
||||
required this.displayName,
|
||||
required this.packageName,
|
||||
required this.minSdkVersion,
|
||||
required this.targetSdkVersion,
|
||||
required this.architectures,
|
||||
});
|
||||
|
||||
/// Converts a `Map<String, dynamic>` to a [ReleaseAnalysis].
|
||||
factory ReleaseAnalysis.fromJson(Map<String, dynamic> json) {
|
||||
return parseFromJson(
|
||||
'ReleaseAnalysis',
|
||||
json,
|
||||
() => ReleaseAnalysis(
|
||||
displayName: json['display_name'] as String,
|
||||
packageName: json['package_name'] as String,
|
||||
minSdkVersion: json['min_sdk_version'] as String,
|
||||
targetSdkVersion: json['target_sdk_version'] as String,
|
||||
architectures: (json['architectures'] as List).cast<String>(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Convenience to create a nullable type from a nullable json object.
|
||||
/// Useful when parsing optional fields.
|
||||
static ReleaseAnalysis? maybeFromJson(Map<String, dynamic>? json) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
return ReleaseAnalysis.fromJson(json);
|
||||
}
|
||||
|
||||
/// The user-visible application name extracted from the artifact
|
||||
/// (e.g. AndroidManifest `application:label`). May differ from
|
||||
/// the user-curated `App.display_name`.
|
||||
final String displayName;
|
||||
|
||||
/// The application package name (e.g. `com.example.app`).
|
||||
final String packageName;
|
||||
|
||||
/// The minimum SDK level required to install the artifact
|
||||
/// (Android API level for android, iOS deployment target for ios).
|
||||
final String minSdkVersion;
|
||||
|
||||
/// The SDK level the artifact targets (Android targetSdk for
|
||||
/// android, iOS SDK for ios).
|
||||
final String targetSdkVersion;
|
||||
|
||||
/// CPU architectures present in the artifact
|
||||
/// (e.g. `["arm64-v8a", "armeabi-v7a"]`).
|
||||
final List<String> architectures;
|
||||
|
||||
/// Converts a [ReleaseAnalysis] to a `Map<String, dynamic>`.
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'display_name': displayName,
|
||||
'package_name': packageName,
|
||||
'min_sdk_version': minSdkVersion,
|
||||
'target_sdk_version': targetSdkVersion,
|
||||
'architectures': architectures,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hashAll([
|
||||
displayName,
|
||||
packageName,
|
||||
minSdkVersion,
|
||||
targetSdkVersion,
|
||||
listHash(architectures),
|
||||
]);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other is ReleaseAnalysis &&
|
||||
displayName == other.displayName &&
|
||||
packageName == other.packageName &&
|
||||
minSdkVersion == other.minSdkVersion &&
|
||||
targetSdkVersion == other.targetSdkVersion &&
|
||||
listsEqual(architectures, other.architectures);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// GENERATED — do not hand-edit.
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('LatestRelease', () {
|
||||
test('round-trips via maybeFromJson/toJson', () {
|
||||
final instance = LatestRelease(
|
||||
id: 0,
|
||||
version: 'example',
|
||||
flutterRevision: 'example',
|
||||
createdAt: DateTime.utc(2024),
|
||||
updatedAt: DateTime.utc(2024),
|
||||
status: ReleaseStatus.values.first,
|
||||
analysis: const ReleaseAnalysis(
|
||||
displayName: 'example',
|
||||
packageName: 'example',
|
||||
minSdkVersion: 'example',
|
||||
targetSdkVersion: 'example',
|
||||
architectures: <String>['example'],
|
||||
),
|
||||
);
|
||||
final parsed = LatestRelease.maybeFromJson(instance.toJson())!;
|
||||
expect(parsed, equals(instance));
|
||||
expect(parsed.hashCode, equals(instance.hashCode));
|
||||
});
|
||||
|
||||
test('maybeFromJson returns null on null input', () {
|
||||
expect(LatestRelease.maybeFromJson(null), isNull);
|
||||
});
|
||||
|
||||
test('maybeFromJson throws FormatException on invalid input', () {
|
||||
expect(
|
||||
() => LatestRelease.maybeFromJson(<String, dynamic>{}),
|
||||
throwsFormatException,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// GENERATED — do not hand-edit.
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('PendingRelease', () {
|
||||
test('round-trips via maybeFromJson/toJson', () {
|
||||
final instance = PendingRelease(
|
||||
id: 0,
|
||||
version: 'example',
|
||||
createdAt: DateTime.utc(2024),
|
||||
);
|
||||
final parsed = PendingRelease.maybeFromJson(instance.toJson())!;
|
||||
expect(parsed, equals(instance));
|
||||
expect(parsed.hashCode, equals(instance.hashCode));
|
||||
});
|
||||
|
||||
test('maybeFromJson returns null on null input', () {
|
||||
expect(PendingRelease.maybeFromJson(null), isNull);
|
||||
});
|
||||
|
||||
test('maybeFromJson throws FormatException on invalid input', () {
|
||||
expect(
|
||||
() => PendingRelease.maybeFromJson(<String, dynamic>{}),
|
||||
throwsFormatException,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// GENERATED — do not hand-edit.
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('ReleaseAnalysis', () {
|
||||
test('round-trips via maybeFromJson/toJson', () {
|
||||
const instance = ReleaseAnalysis(
|
||||
displayName: 'example',
|
||||
packageName: 'example',
|
||||
minSdkVersion: 'example',
|
||||
targetSdkVersion: 'example',
|
||||
architectures: <String>['example'],
|
||||
);
|
||||
final parsed = ReleaseAnalysis.maybeFromJson(instance.toJson())!;
|
||||
expect(parsed, equals(instance));
|
||||
expect(parsed.hashCode, equals(instance.hashCode));
|
||||
});
|
||||
|
||||
test('maybeFromJson returns null on null input', () {
|
||||
expect(ReleaseAnalysis.maybeFromJson(null), isNull);
|
||||
});
|
||||
|
||||
test('maybeFromJson throws FormatException on invalid input', () {
|
||||
expect(
|
||||
() => ReleaseAnalysis.maybeFromJson(<String, dynamic>{}),
|
||||
throwsFormatException,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -18,6 +18,49 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
test('can be (de)serialized with platforms, latestReleases, '
|
||||
'pendingReleases, and iconUrl', () {
|
||||
final appMetadata = AppMetadata(
|
||||
appId: '30370f27-dbf1-4673-8b20-fb096e38dffa',
|
||||
displayName: 'My App',
|
||||
latestReleaseVersion: '1.0.0',
|
||||
latestPatchNumber: 1,
|
||||
createdAt: DateTime(2022),
|
||||
updatedAt: DateTime(2023),
|
||||
platforms: const [ReleasePlatform.android, ReleasePlatform.ios],
|
||||
latestReleases: {
|
||||
ReleasePlatform.android: LatestRelease(
|
||||
id: 535,
|
||||
version: '1.2.3+5',
|
||||
flutterRevision: 'abc123',
|
||||
flutterVersion: '3.27.0',
|
||||
createdAt: DateTime(2023),
|
||||
updatedAt: DateTime(2023),
|
||||
status: ReleaseStatus.active,
|
||||
analysis: const ReleaseAnalysis(
|
||||
displayName: 'My App',
|
||||
packageName: 'com.example.app',
|
||||
minSdkVersion: '24',
|
||||
targetSdkVersion: '34',
|
||||
architectures: ['arm64-v8a'],
|
||||
),
|
||||
),
|
||||
},
|
||||
pendingReleases: {
|
||||
ReleasePlatform.ios: PendingRelease(
|
||||
id: 700,
|
||||
version: '1.2.4+6',
|
||||
createdAt: DateTime(2024),
|
||||
),
|
||||
},
|
||||
iconUrl: '/api/v1/app-icons/30370f27-dbf1-4673-8b20-fb096e38dffa?v=535',
|
||||
);
|
||||
expect(
|
||||
AppMetadata.fromJson(appMetadata.toJson()).toJson(),
|
||||
equals(appMetadata.toJson()),
|
||||
);
|
||||
});
|
||||
|
||||
group('equality', () {
|
||||
test('should return true if all properties are equal', () {
|
||||
final appMetadata1 = AppMetadata(
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group(LatestRelease, () {
|
||||
test('can be (de)serialized', () {
|
||||
final latest = LatestRelease(
|
||||
id: 535,
|
||||
version: '1.2.3+5',
|
||||
flutterRevision: 'abc123',
|
||||
flutterVersion: '3.27.0',
|
||||
createdAt: DateTime(2023, 3),
|
||||
updatedAt: DateTime(2023, 4),
|
||||
status: ReleaseStatus.active,
|
||||
notes: 'launch notes',
|
||||
analysis: const ReleaseAnalysis(
|
||||
displayName: 'My App',
|
||||
packageName: 'com.example.app',
|
||||
minSdkVersion: '24',
|
||||
targetSdkVersion: '34',
|
||||
architectures: ['arm64-v8a'],
|
||||
),
|
||||
);
|
||||
expect(
|
||||
LatestRelease.fromJson(latest.toJson()).toJson(),
|
||||
equals(latest.toJson()),
|
||||
);
|
||||
});
|
||||
|
||||
test('is equatable', () {
|
||||
const analysis = ReleaseAnalysis(
|
||||
displayName: 'My App',
|
||||
packageName: 'com.example.app',
|
||||
minSdkVersion: '24',
|
||||
targetSdkVersion: '34',
|
||||
architectures: ['arm64-v8a'],
|
||||
);
|
||||
final latest1 = LatestRelease(
|
||||
id: 535,
|
||||
version: '1.2.3+5',
|
||||
flutterRevision: 'abc123',
|
||||
createdAt: DateTime(2023, 3),
|
||||
updatedAt: DateTime(2023, 4),
|
||||
status: ReleaseStatus.active,
|
||||
analysis: analysis,
|
||||
);
|
||||
final latest1Copy = LatestRelease(
|
||||
id: 535,
|
||||
version: '1.2.3+5',
|
||||
flutterRevision: 'abc123',
|
||||
createdAt: DateTime(2023, 3),
|
||||
updatedAt: DateTime(2023, 4),
|
||||
status: ReleaseStatus.active,
|
||||
analysis: analysis,
|
||||
);
|
||||
final latest2 = LatestRelease(
|
||||
id: 600,
|
||||
version: '1.2.4+6',
|
||||
flutterRevision: 'abc123',
|
||||
createdAt: DateTime(2023, 3),
|
||||
updatedAt: DateTime(2023, 4),
|
||||
status: ReleaseStatus.active,
|
||||
analysis: analysis,
|
||||
);
|
||||
|
||||
expect(latest1, equals(latest1Copy));
|
||||
expect(latest1, isNot(equals(latest2)));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group(PendingRelease, () {
|
||||
test('can be (de)serialized', () {
|
||||
final pending = PendingRelease(
|
||||
id: 537,
|
||||
version: '1.2.4+6',
|
||||
createdAt: DateTime(2024),
|
||||
);
|
||||
expect(
|
||||
PendingRelease.fromJson(pending.toJson()).toJson(),
|
||||
equals(pending.toJson()),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group(ReleaseAnalysis, () {
|
||||
test('can be (de)serialized', () {
|
||||
const analysis = ReleaseAnalysis(
|
||||
displayName: 'My App',
|
||||
packageName: 'com.example.app',
|
||||
minSdkVersion: '24',
|
||||
targetSdkVersion: '34',
|
||||
architectures: ['arm64-v8a', 'armeabi-v7a'],
|
||||
);
|
||||
expect(
|
||||
ReleaseAnalysis.fromJson(analysis.toJson()).toJson(),
|
||||
equals(analysis.toJson()),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user