require 'spec_helper' describe Csvlint::Schema do it "should tolerate missing fields" do schema = Csvlint::Schema.from_json_table("http://example.org", {}) expect( schema ).to_not be(nil) expect( schema.fields.empty? ).to eql(true) end it "should tolerate fields with no constraints" do schema = Csvlint::Schema.from_json_table("http://example.org", { "fields" => [ { "name" => "test" } ] }) expect( schema ).to_not be(nil) expect( schema.fields[0].name ).to eql("test") expect( schema.fields[0].constraints ).to eql({}) end it "should validate against the schema" do field = Csvlint::Field.new("test", { "required" => true } ) field2 = Csvlint::Field.new("test", { "minLength" => 3 } ) schema = Csvlint::Schema.new("http://example.org", [field, field2] ) expect( schema.validate_row( ["", "x"] ) ).to eql(false) expect( schema.errors.size ).to eql(2) expect( schema.errors.first.type).to eql(:missing_value) expect( schema.errors.first.category).to eql(:schema) expect( schema.errors.first.column).to eql(1) expect( schema.validate_row( ["abc", "1234"] ) ).to eql(true) end it "should include validations for missing columns" do minimum = Csvlint::Field.new("test", { "minLength" => 3 } ) required = Csvlint::Field.new("test2", { "required" => true } ) schema = Csvlint::Schema.new("http://example.org", [minimum, required] ) expect( schema.validate_row( ["abc", "x"] ) ).to eql(true) expect( schema.validate_row( ["abc"] ) ).to eql(false) expect( schema.errors.size ).to eql(1) expect( schema.errors.first.type).to eql(:missing_value) schema = Csvlint::Schema.new("http://example.org", [required, minimum] ) expect( schema.validate_row( ["abc"] ) ).to eql(false) expect( schema.errors.size ).to eql(1) expect( schema.errors.first.type).to eql(:min_length) end it "should warn if the data has fewer columns" do minimum = Csvlint::Field.new("test", { "minLength" => 3 } ) required = Csvlint::Field.new("test2", { "maxLength" => 5 } ) schema = Csvlint::Schema.new("http://example.org", [minimum, required] ) expect( schema.validate_row( ["abc"], 1 ) ).to eql(true) expect( schema.warnings.size ).to eql(1) expect( schema.warnings.first.type).to eql(:missing_column) expect( schema.warnings.first.category).to eql(:schema) expect( schema.warnings.first.row).to eql(1) expect( schema.warnings.first.column).to eql(2) #no ragged row error expect( schema.errors.size ).to eql(0) end it "should warn if the data has additional columns" do minimum = Csvlint::Field.new("test", { "minLength" => 3 } ) required = Csvlint::Field.new("test2", { "required" => true } ) schema = Csvlint::Schema.new("http://example.org", [minimum, required] ) expect( schema.validate_row( ["abc", "x", "more", "columns"], 1 ) ).to eql(true) expect( schema.warnings.size ).to eql(2) expect( schema.warnings.first.type).to eql(:extra_column) expect( schema.warnings.first.category).to eql(:schema) expect( schema.warnings.first.row).to eql(1) expect( schema.warnings.first.column).to eql(3) expect( schema.warnings[1].type).to eql(:extra_column) expect( schema.warnings[1].column).to eql(4) #no ragged row error expect( schema.errors.size ).to eql(0) end context "when validating header" do it "should warn if column names are different to field names" do minimum = Csvlint::Field.new("minimum", { "minLength" => 3 } ) required = Csvlint::Field.new("required", { "required" => true } ) schema = Csvlint::Schema.new("http://example.org", [minimum, required] ) expect( schema.validate_header(["minimum", "required"]) ).to eql(true) expect( schema.warnings.size ).to eql(0) expect( schema.validate_header(["wrong", "required"]) ).to eql(true) expect( schema.warnings.size ).to eql(1) expect( schema.warnings.first.row ).to eql(1) expect( schema.warnings.first.type ).to eql(:malformed_header) expect( schema.warnings.first.content ).to eql('wrong,required') expect( schema.warnings.first.column ).to eql(nil) expect( schema.warnings.first.category ).to eql(:schema) expect schema.warnings.first.constraints.has_value?('minimum,required') # expect( schema.warnings.first.constraints.values ).to eql(['minimum,required']) expect( schema.validate_header(["minimum", "Required"]) ).to eql(true) expect( schema.warnings.size ).to eql(1) end it "should warn if column count is less than field count" do minimum = Csvlint::Field.new("minimum", { "minLength" => 3 } ) required = Csvlint::Field.new("required", { "required" => true } ) schema = Csvlint::Schema.new("http://example.org", [minimum, required] ) expect( schema.validate_header(["minimum"]) ).to eql(true) expect( schema.warnings.size ).to eql(1) expect( schema.warnings.first.row ).to eql(1) expect( schema.warnings.first.type ).to eql(:malformed_header) expect( schema.warnings.first.content ).to eql("minimum") expect( schema.warnings.first.column ).to eql(nil) expect( schema.warnings.first.category ).to eql(:schema) expect schema.warnings.first.constraints.has_value?('minimum,required') # expect( schema.warnings.first.constraints.values ).to eql(['minimum,required']) end it "should warn if column count is more than field count" do minimum = Csvlint::Field.new("minimum", { "minLength" => 3 } ) schema = Csvlint::Schema.new("http://example.org", [minimum] ) expect( schema.validate_header(["wrong", "required"]) ).to eql(true) expect( schema.warnings.size ).to eql(1) expect( schema.warnings.first.row ).to eql(1) expect( schema.warnings.first.type ).to eql(:malformed_header) expect( schema.warnings.first.content ).to eql("wrong,required") expect( schema.warnings.first.column ).to eql(nil) expect( schema.warnings.first.category ).to eql(:schema) # expect( schema.warnings.first.constraints.values ).to eql('minimum') expect( schema.warnings.first.constraints.has_value?('minimum')) end end context "when parsing JSON Tables" do before(:each) do @example=<<-EOL { "title": "Schema title", "description": "schema", "fields": [ { "name": "ID", "constraints": { "required": true }, "title": "id", "description": "house identifier" }, { "name": "Price", "constraints": { "required": true, "minLength": 1 } }, { "name": "Postcode", "constraints": { "required": true, "pattern": "[A-Z]{1,2}[0-9][0-9A-Z]? ?[0-9][A-Z]{2}" } } ] } EOL stub_request(:get, "http://example.com/example.json").to_return(:status => 200, :body => @example) end it "should create a schema from a pre-parsed JSON table" do json = JSON.parse( @example ) schema = Csvlint::Schema.from_json_table("http://example.org", json) expect( schema.uri ).to eql("http://example.org") expect( schema.title ).to eql("Schema title") expect( schema.description ).to eql("schema") expect( schema.fields.length ).to eql(3) expect( schema.fields[0].name ).to eql("ID") expect( schema.fields[0].constraints["required"] ).to eql(true) expect( schema.fields[0].title ).to eql("id") expect( schema.fields[0].description ).to eql("house identifier") expect( schema.fields[2].constraints["pattern"]).to eql("[A-Z]{1,2}[0-9][0-9A-Z]? ?[0-9][A-Z]{2}") end it "should create a schema from a JSON Table URL" do schema = Csvlint::Schema.load_from_json("http://example.com/example.json") expect( schema.uri ).to eql("http://example.com/example.json") expect( schema.fields.length ).to eql(3) expect( schema.fields[0].name ).to eql("ID") expect( schema.fields[0].constraints["required"] ).to eql(true) end end context "when parsing CSVW metadata" do before(:each) do @example=<<-EOL { "@context": "http://www.w3.org/ns/csvw", "url": "http://example.com/example1.csv", "tableSchema": { "columns": [ { "name": "Name", "required": true, "datatype": { "base": "string", "format": ".+" } }, { "name": "Id", "required": true, "datatype": { "base": "string", "minLength": 3 } }, { "name": "Email", "required": true } ] } } EOL stub_request(:get, "http://example.com/metadata.json").to_return(:status => 200, :body => @example) end it "should create a table group from a CSVW metadata URL" do schema = Csvlint::Schema.load_from_json("http://example.com/metadata.json") expect( schema.class ).to eq(Csvlint::Csvw::TableGroup) end end end