blob: 1694bd53cf93affb32e04146ba1b69bab131baf8 [file] [log] [blame] [edit]
//
// MTLJSONAdapterSpec.m
// Mantle
//
// Created by Justin Spahr-Summers on 2013-02-13.
// Copyright (c) 2013 GitHub. All rights reserved.
//
#import "MTLTestModel.h"
SpecBegin(MTLJSONAdapter)
it(@"should initialize from JSON", ^{
NSDictionary *values = @{
@"username": NSNull.null,
@"count": @"5",
};
NSError *error = nil;
MTLJSONAdapter *adapter = [[MTLJSONAdapter alloc] initWithJSONDictionary:values modelClass:MTLTestModel.class error:&error];
expect(adapter).notTo.beNil();
expect(error).to.beNil();
MTLTestModel *model = (id)adapter.model;
expect(model).notTo.beNil();
expect(model.name).to.beNil();
expect(model.count).to.equal(5);
NSDictionary *JSONDictionary = @{
@"username": NSNull.null,
@"count": @"5",
@"nested": @{ @"name": NSNull.null },
};
expect(adapter.JSONDictionary).to.equal(JSONDictionary);
});
it(@"should initialize from a model", ^{
MTLTestModel *model = [MTLTestModel modelWithDictionary:@{
@"name": @"foobar",
@"count": @5,
} error:NULL];
MTLJSONAdapter *adapter = [[MTLJSONAdapter alloc] initWithModel:model];
expect(adapter).notTo.beNil();
expect(adapter.model).to.beIdenticalTo(model);
NSDictionary *JSONDictionary = @{
@"username": @"foobar",
@"count": @"5",
@"nested": @{ @"name": NSNull.null },
};
expect(adapter.JSONDictionary).to.equal(JSONDictionary);
});
it(@"should initialize nested key paths from JSON", ^{
NSDictionary *values = @{
@"username": @"foo",
@"nested": @{ @"name": @"bar" },
@"count": @"0"
};
NSError *error = nil;
MTLTestModel *model = [MTLJSONAdapter modelOfClass:MTLTestModel.class fromJSONDictionary:values error:&error];
expect(model).notTo.beNil();
expect(error).to.beNil();
expect(model.name).to.equal(@"foo");
expect(model.count).to.equal(0);
expect(model.nestedName).to.equal(@"bar");
expect([MTLJSONAdapter JSONDictionaryFromModel:model]).to.equal(values);
});
it(@"should return nil and error with an invalid key path from JSON",^{
NSDictionary *values = @{
@"username": @"foo",
@"nested": @"bar",
@"count": @"0"
};
NSError *error = nil;
MTLTestModel *model = [MTLJSONAdapter modelOfClass:MTLTestModel.class fromJSONDictionary:values error:&error];
expect(model).beNil();
expect(error).notTo.beNil();
expect(error.domain).to.equal(MTLJSONAdapterErrorDomain);
expect(error.code).to.equal(MTLJSONAdapterErrorInvalidJSONDictionary);
});
it(@"should return nil and error with an illegal JSON mapping", ^{
NSDictionary *values = @{
@"username": @"foo"
};
NSError *error = nil;
MTLIllegalJSONMappingModel *model = [MTLJSONAdapter modelOfClass:MTLIllegalJSONMappingModel.class fromJSONDictionary:values error:&error];
expect(model).beNil();
expect(error).notTo.beNil();
expect(error.domain).to.equal(MTLJSONAdapterErrorDomain);
expect(error.code).to.equal(MTLJSONAdapterErrorInvalidJSONMapping);
});
it(@"should support key paths across arrays", ^{
NSDictionary *values = @{
@"users": @[
@{
@"name": @"foo"
},
@{
@"name": @"bar"
},
@{
@"name": @"baz"
}
]
};
NSError *error = nil;
MTLArrayTestModel *model = [MTLJSONAdapter modelOfClass:MTLArrayTestModel.class fromJSONDictionary:values error:&error];
expect(model).notTo.beNil();
expect(error).to.beNil();
expect(model.names).to.equal((@[ @"foo", @"bar", @"baz" ]));
});
it(@"should initialize without returning any error when using a JSON dictionary which Null.null as value",^{
NSDictionary *values = @{
@"username": @"foo",
@"nested": NSNull.null,
@"count": @"0"
};
NSError *error = nil;
MTLTestModel *model = [MTLJSONAdapter modelOfClass:MTLTestModel.class fromJSONDictionary:values error:&error];
expect(model).notTo.beNil();
expect(error).to.beNil();
expect(model.name).to.equal(@"foo");
expect(model.count).to.equal(0);
expect(model.nestedName).to.beNil();
});
it(@"should return nil and an error with a nil JSON dictionary", ^{
NSError *error = nil;
MTLJSONAdapter *adapter = [[MTLJSONAdapter alloc] initWithJSONDictionary:nil modelClass:MTLTestModel.class error:&error];
expect(adapter).to.beNil();
expect(error).notTo.beNil();
expect(error.domain).to.equal(MTLJSONAdapterErrorDomain);
expect(error.code).to.equal(MTLJSONAdapterErrorInvalidJSONDictionary);
});
it(@"should return nil and an error with a wrong data type as dictionary", ^{
NSError *error = nil;
id wrongDictionary = @"";
MTLJSONAdapter *adapter = [[MTLJSONAdapter alloc] initWithJSONDictionary:wrongDictionary modelClass:MTLTestModel.class error:&error];
expect(adapter).to.beNil();
expect(error).notTo.beNil();
expect(error.domain).to.equal(MTLJSONAdapterErrorDomain);
expect(error.code).to.equal(MTLJSONAdapterErrorInvalidJSONDictionary);
});
it(@"should ignore unrecognized JSON keys", ^{
NSDictionary *values = @{
@"foobar": @"foo",
@"count": @"2",
@"_": NSNull.null,
@"username": @"buzz",
@"nested": @{ @"name": @"bar", @"stuffToIgnore": @5, @"moreNonsense": NSNull.null },
};
NSError *error = nil;
MTLTestModel *model = [MTLJSONAdapter modelOfClass:MTLTestModel.class fromJSONDictionary:values error:&error];
expect(model).notTo.beNil();
expect(error).to.beNil();
expect(model.name).to.equal(@"buzz");
expect(model.count).to.equal(2);
expect(model.nestedName).to.equal(@"bar");
});
it(@"should fail to initialize if JSON dictionary validation fails", ^{
NSDictionary *values = @{
@"username": @"this is too long a name",
};
NSError *error = nil;
MTLTestModel *model = [MTLJSONAdapter modelOfClass:MTLTestModel.class fromJSONDictionary:values error:&error];
expect(model).to.beNil();
expect(error.domain).to.equal(MTLTestModelErrorDomain);
expect(error.code).to.equal(MTLTestModelNameTooLong);
});
it(@"should parse a different model class", ^{
NSDictionary *values = @{
@"username": @"foo",
@"nested": @{ @"name": @"bar" },
@"count": @"0"
};
NSError *error = nil;
MTLTestModel *model = [MTLJSONAdapter modelOfClass:MTLSubstitutingTestModel.class fromJSONDictionary:values error:&error];
expect(model).to.beKindOf(MTLTestModel.class);
expect(error).to.beNil();
expect(model.name).to.equal(@"foo");
expect(model.count).to.equal(0);
expect(model.nestedName).to.equal(@"bar");
expect([MTLJSONAdapter JSONDictionaryFromModel:model]).to.equal(values);
});
it(@"should return an error when no suitable model class is found", ^{
NSError *error = nil;
MTLTestModel *model = [MTLJSONAdapter modelOfClass:MTLSubstitutingTestModel.class fromJSONDictionary:@{} error:&error];
expect(model).to.beNil();
expect(error).notTo.beNil();
expect(error.domain).to.equal(MTLJSONAdapterErrorDomain);
expect(error.code).to.equal(MTLJSONAdapterErrorNoClassFound);
});
describe(@"Deserializing multiple models", ^{
NSDictionary *value1 = @{
@"username": @"foo"
};
NSDictionary *value2 = @{
@"username": @"bar"
};
NSArray *JSONModels = @[ value1, value2 ];
it(@"should initialize models from an array of JSON dictionaries", ^{
NSError *error = nil;
NSArray *mantleModels = [MTLJSONAdapter modelsOfClass:MTLTestModel.class fromJSONArray:JSONModels error:&error];
expect(error).to.beNil();
expect(mantleModels).toNot.beNil();
expect(mantleModels).haveCountOf(2);
expect([mantleModels[0] name]).to.equal(@"foo");
expect([mantleModels[1] name]).to.equal(@"bar");
});
it(@"should not be affected by a NULL error parameter", ^{
NSError *error = nil;
NSArray *expected = [MTLJSONAdapter modelsOfClass:MTLTestModel.class fromJSONArray:JSONModels error:&error];
NSArray *models = [MTLJSONAdapter modelsOfClass:MTLTestModel.class fromJSONArray:JSONModels error:NULL];
expect(models).to.equal(expected);
});
});
it(@"should return nil and an error if it fails to initialize any model from an array", ^{
NSDictionary *value1 = @{
@"username": @"foo",
@"count": @"1",
};
NSDictionary *value2 = @{
@"count": @[ @"This won't parse" ],
};
NSArray *JSONModels = @[ value1, value2 ];
NSError *error = nil;
NSArray *mantleModels = [MTLJSONAdapter modelsOfClass:MTLSubstitutingTestModel.class fromJSONArray:JSONModels error:&error];
expect(error).toNot.beNil();
expect(error.domain).to.equal(MTLJSONAdapterErrorDomain);
expect(error.code).to.equal(MTLJSONAdapterErrorNoClassFound);
expect(mantleModels).to.beNil();
});
it(@"should return an array of dictionaries from models", ^{
MTLTestModel *model1 = [[MTLTestModel alloc] init];
model1.name = @"foo";
MTLTestModel *model2 = [[MTLTestModel alloc] init];
model2.name = @"bar";
NSArray *JSONArray = [MTLJSONAdapter JSONArrayFromModels:@[ model1, model2 ]];
expect(JSONArray).toNot.beNil();
expect(JSONArray).haveCountOf(2);
expect(JSONArray[0][@"username"]).to.equal(@"foo");
expect(JSONArray[1][@"username"]).to.equal(@"bar");
});
SpecEnd