fix: validate AniList reports against version two schema
This commit is contained in:
@@ -37,25 +37,37 @@ static NSString *nativePath(const std::string &path) {
|
||||
return [NSString stringWithUTF8String:path.c_str()];
|
||||
}
|
||||
|
||||
static NSArray<NSDictionary *> *records(const std::string &path) {
|
||||
static NSArray<NSDictionary *> *records(const std::string &path, int version = 1) {
|
||||
NSString *text = [NSString stringWithContentsOfFile:nativePath(path)
|
||||
encoding:NSUTF8StringEncoding error:nullptr];
|
||||
require(text != nil && [text hasSuffix:@"\n"], "finished JSONL has a complete final line");
|
||||
NSMutableArray<NSDictionary *> *result = [NSMutableArray array];
|
||||
NSArray<NSString *> *lines = [text componentsSeparatedByString:@"\n"];
|
||||
NSSet *keys = [NSSet setWithArray:@[@"version", @"event", @"media_url", @"position_seconds",
|
||||
@"duration_seconds", @"completion_eligible"]];
|
||||
NSSet *keys = [NSSet setWithArray:@[@"version", @"event", version == 2 ? @"media_identity" : @"media_url",
|
||||
@"position_seconds", @"duration_seconds", @"completion_eligible"]];
|
||||
for (NSUInteger i = 0; i + 1 < lines.count; ++i) {
|
||||
id object = [NSJSONSerialization JSONObjectWithData:[lines[i] dataUsingEncoding:NSUTF8StringEncoding]
|
||||
options:0 error:nullptr];
|
||||
require([object isKindOfClass:[NSDictionary class]], "consumer decodes every JSONL record");
|
||||
NSDictionary *row = object;
|
||||
require([[NSSet setWithArray:row.allKeys] isEqual:keys], "records have exactly the six specified fields");
|
||||
require([row[@"version"] isKindOfClass:[NSNumber class]] && [row[@"version"] intValue] == 1,
|
||||
"schema version is numeric one");
|
||||
require([row[@"version"] isKindOfClass:[NSNumber class]] && [row[@"version"] intValue] == version,
|
||||
"schema version matches the expected source contract");
|
||||
require([@[@"progress", @"ended", @"closed"] containsObject:row[@"event"]], "known event type");
|
||||
require([row[@"media_url"] isKindOfClass:[NSString class]] &&
|
||||
ProgressReporter::validMediaURL([row[@"media_url"] UTF8String]), "HTTP(S) consumer identity");
|
||||
if (version == 2) {
|
||||
NSDictionary *identity = row[@"media_identity"];
|
||||
require([identity isKindOfClass:[NSDictionary class]] &&
|
||||
[[NSSet setWithArray:identity.allKeys] isEqual:
|
||||
[NSSet setWithArray:@[@"provider", @"media_id", @"episode"]]] &&
|
||||
[identity[@"provider"] isKindOfClass:[NSString class]] &&
|
||||
[identity[@"media_id"] isKindOfClass:[NSString class]] &&
|
||||
[identity[@"episode"] isKindOfClass:[NSNumber class]], "structured episode consumer identity");
|
||||
NSString *key = [NSString stringWithFormat:@"%@:%@:%@", identity[@"provider"], identity[@"media_id"], identity[@"episode"]];
|
||||
require(ProgressReporter::validMediaIdentity(key.UTF8String), "canonical episode consumer identity");
|
||||
} else {
|
||||
require([row[@"media_url"] isKindOfClass:[NSString class]] &&
|
||||
ProgressReporter::validMediaURL([row[@"media_url"] UTF8String]), "HTTP(S) consumer identity");
|
||||
}
|
||||
require([row[@"position_seconds"] isKindOfClass:[NSNumber class]] &&
|
||||
std::isfinite([row[@"position_seconds"] doubleValue]) &&
|
||||
[row[@"position_seconds"] doubleValue] >= 0, "position is finite and nonnegative");
|
||||
@@ -316,7 +328,7 @@ int main(int argc, char **argv) {
|
||||
reporter.finish();
|
||||
require(reporter.error().empty(), "AniList progress drains successfully");
|
||||
}
|
||||
rows = records(anilistPath);
|
||||
rows = records(anilistPath, 2);
|
||||
require(rows.count == 2, "AniList initial and closed snapshots");
|
||||
for (NSDictionary *row in rows) {
|
||||
require([row[@"version"] intValue] == 2 && row[@"media_url"] == nil &&
|
||||
|
||||
Reference in New Issue
Block a user