From 97f88f9df8000374c7f296d678da715e63049385 Mon Sep 17 00:00:00 2001 From: Chris Watson Date: Fri, 27 Jan 2023 11:26:11 -0700 Subject: [PATCH] testing stuff --- README.md | 8 ++-- spec/arg_parser_spec.cr | 37 ------------------- spec/spec_helper.cr | 25 +++++++++++++ spec/unit/arg_parser_spec.cr | 35 ++++++++++++++++++ spec/unit/converter_spec.cr | 26 +++++++++++++ src/arg_parser.cr | 12 ++++-- .../converters/enum_value_converter.cr | 2 +- src/arg_parser/converters/epoch_converter.cr | 2 +- .../converters/epoch_ms_converter.cr | 2 +- src/arg_parser/errors.cr | 5 ++- 10 files changed, 104 insertions(+), 50 deletions(-) delete mode 100644 spec/arg_parser_spec.cr create mode 100644 spec/unit/arg_parser_spec.cr create mode 100644 spec/unit/converter_spec.cr diff --git a/README.md b/README.md index da2926b..1ccafd0 100644 --- a/README.md +++ b/README.md @@ -155,10 +155,10 @@ args._validation_errors # => {"age" => ["must be an integer"]} ArgParser is designed to be configurable so it can handle a wide variety of use cases. As such, it includes several overridable methods which can be used to modify its behavior. These are: -- `on_validation_error` - called when a validation error occurs -- `on_unknown_attribute` - called when an unknown attribute is encountered -- `on_missing_attribute` - called when a required attribute is missing -- `on_conversion_error` - called when a value isn't able to be converted to the specified type +- `on_validation_error` - called when a validation error occurs; by default calls `add_validation_error` and then raises `ArgParser::ValidationError` +- `on_unknown_attribute` - called when an unknown attribute is encountered; by default raises `ArgParser::UnknownAttributeError` +- `on_missing_attribute` - called when a required attribute is missing; by default raises `ArgParser::MissingAttributeError` +- `on_conversion_error` - called when a value isn't able to be converted to the specified type; by default raises `ArgParser::ConversionError` In addition, the way keys are parsed can be modified by overriding the `parse_key` method. By default, it simply removes one or two dashes from the beginning of the key. For example, `--name` becomes `name`, and `-n` becomes `n`. diff --git a/spec/arg_parser_spec.cr b/spec/arg_parser_spec.cr deleted file mode 100644 index b095f47..0000000 --- a/spec/arg_parser_spec.cr +++ /dev/null @@ -1,37 +0,0 @@ -require "./spec_helper" - -Spectator.describe ArgParser do - context "supported arguments" do - let(args) { ["--name", "John Doe", "--age", "32", "--height", "163.2", "--is_human", "true", "--website", "https://example.com", "--id", "39aacd14-9e1b-11ed-91ad-b44506ca30d5"] } - let(parser) { TestSupportedArgs.new(args) } - - it "parses name" do - expect(parser.name).to eq("John Doe") - end - - it "parses age" do - expect(parser.age).to be_a(Int32) - expect(parser.age).to eq(32) - end - - it "parses height" do - expect(parser.height).to be_a(Float64) - expect(parser.height).to eq(163.2) - end - - it "parses is_human" do - expect(parser.is_human).to be_a(Bool) - expect(parser.is_human).to eq(true) - end - - it "parses website" do - expect(parser.website).to be_a(URI) - expect(parser.website.to_s).to eq("https://example.com") - end - - it "parses id" do - expect(parser.id).to be_a(UUID) - expect(parser.id.to_s).to eq("39aacd14-9e1b-11ed-91ad-b44506ca30d5") - end - end -end diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr index 2c88dad..3b92606 100644 --- a/spec/spec_helper.cr +++ b/spec/spec_helper.cr @@ -19,3 +19,28 @@ struct TestSupportedArgs getter id : UUID end + +enum Color + Red + Green + Blue +end + +struct TestConverters + include ArgParser + + @[ArgParser::Field(converter: ArgParser::CommaSeparatedArrayConverter(Int32))] + getter ids : Array(Int32) + + @[ArgParser::Field(converter: ArgParser::EpochConverter)] + getter unix : Time + + @[ArgParser::Field(converter: ArgParser::EpochMillisConverter, key: "unix-ms")] + getter unix_ms : Time + + @[ArgParser::Field(converter: ArgParser::EnumNameConverter(Color))] + getter color : Color + + @[ArgParser::Field(converter: ArgParser::EnumValueConverter(Color))] + getter ncolor : Color +end diff --git a/spec/unit/arg_parser_spec.cr b/spec/unit/arg_parser_spec.cr new file mode 100644 index 0000000..269468d --- /dev/null +++ b/spec/unit/arg_parser_spec.cr @@ -0,0 +1,35 @@ +require "../spec_helper" + +Spectator.describe ArgParser do + let(args) { ["--name", "John Doe", "--age", "32", "--height", "163.2", "--is_human", "--website", "https://example.com", "--id", "39aacd14-9e1b-11ed-91ad-b44506ca30d5"] } + let(parser) { TestSupportedArgs.new(args) } + + it "parses string values" do + expect(parser.name).to eq("John Doe") + end + + it "parses int32 values" do + expect(parser.age).to be_a(Int32) + expect(parser.age).to eq(32) + end + + it "parses float64 value" do + expect(parser.height).to be_a(Float64) + expect(parser.height).to eq(163.2) + end + + it "parses boolean values" do + expect(parser.is_human).to be_a(Bool) + expect(parser.is_human).to eq(true) + end + + it "parses uri values" do + expect(parser.website).to be_a(URI) + expect(parser.website.to_s).to eq("https://example.com") + end + + it "parses uuid values" do + expect(parser.id).to be_a(UUID) + expect(parser.id.to_s).to eq("39aacd14-9e1b-11ed-91ad-b44506ca30d5") + end +end diff --git a/spec/unit/converter_spec.cr b/spec/unit/converter_spec.cr new file mode 100644 index 0000000..34d2a53 --- /dev/null +++ b/spec/unit/converter_spec.cr @@ -0,0 +1,26 @@ +require "../spec_helper" + +Spectator.describe "ArgParser converters" do + let(args) { ["--ids", "1,2,3,4", "--unix", "1674843165", "--unix-ms", "1674843241159", "--color", "red", "--ncolor", "2"] } + let(parser) { TestConverters.new(args) } + + it "converts comma separated list to array" do + expect(parser.ids).to eq [1, 2, 3, 4] + end + + it "converts unix time to time" do + expect(parser.unix).to eq Time.unix(1674843165) + end + + it "converts unix time to time" do + expect(parser.unix_ms).to eq Time.unix_ms(1674843241159) + end + + it "converts string to color" do + expect(parser.color).to eq Color::Red + end + + it "converts string to color" do + expect(parser.ncolor).to eq Color::Blue + end +end diff --git a/src/arg_parser.cr b/src/arg_parser.cr index f00289f..7ed290d 100644 --- a/src/arg_parser.cr +++ b/src/arg_parser.cr @@ -84,7 +84,7 @@ module ArgParser {% elsif value[:type] < Array %} %var{name} ||= [] of {{value[:type].type_vars[0]}} {% if value[:converter] %} - %var{name} << {{ value[:converter] }}.from_arg(value) + %var{name}.concat {{ value[:converter] }}.from_arg(value) {% else %} %var{name} << ::Union({{value[:type].type_vars[0]}}).from_arg(value) {% end %} @@ -146,8 +146,6 @@ module ArgParser %validator{name} = {{ validator.name(generic_args: false) }}.new({{ args.join(", ").id }}) if %found{name} && !%validator{name}.validate({{name.id.stringify}}, @{{name}}) - @_validation_errors[{{name.stringify}}] ||= [] of String - @_validation_errors[{{name.stringify}}] += %validator{name}.errors on_validation_error({{name.stringify}}, @{{name}}, %validator{name}.errors) end {% end %} @@ -189,7 +187,8 @@ module ArgParser # # Note: You can override this method to change the way validation errors are handled. def on_validation_error(key : String, value, errors : Array(String)) - raise ValidationError.new(key, errors) + add_validation_error(key, erorrs) + raise ValidationError.new(key, value, errors) end # Called when a value cannot be converted to the expected type. @@ -199,6 +198,11 @@ module ArgParser raise ConversionError.new(key, value, type) end + def add_validation_error(key : String, value, errors : Array(String)) + @errors[key] ||= [] of String + @errors[key].concat errors + end + # https://en.wikipedia.org/wiki/Quotation_mark#Summary_table QUOTE_CHARS = {'"' => '"', '“' => '”', '‘' => '’', '«' => '»', '‹' => '›', '❛' => '❜', '❝' => '❞', '❮' => '❯', '"' => '"'} diff --git a/src/arg_parser/converters/enum_value_converter.cr b/src/arg_parser/converters/enum_value_converter.cr index 6db39b7..cf0ec24 100644 --- a/src/arg_parser/converters/enum_value_converter.cr +++ b/src/arg_parser/converters/enum_value_converter.cr @@ -1,5 +1,5 @@ module ArgParser::EnumValueConverter(E) def self.from_arg(arg) - E.new(arg) + E.from_value(arg.to_i64) end end diff --git a/src/arg_parser/converters/epoch_converter.cr b/src/arg_parser/converters/epoch_converter.cr index 273b03d..8462649 100644 --- a/src/arg_parser/converters/epoch_converter.cr +++ b/src/arg_parser/converters/epoch_converter.cr @@ -1,5 +1,5 @@ module ArgParser::EpochConverter def self.from_arg(arg) - Time.epoch(arg.to_i64) + Time.unix(arg.to_i64) end end diff --git a/src/arg_parser/converters/epoch_ms_converter.cr b/src/arg_parser/converters/epoch_ms_converter.cr index cebb21f..4c477fa 100644 --- a/src/arg_parser/converters/epoch_ms_converter.cr +++ b/src/arg_parser/converters/epoch_ms_converter.cr @@ -1,5 +1,5 @@ module ArgParser::EpochMillisConverter def self.from_arg(arg) - Time.epoch_ms(arg.to_i64) + Time.unix_ms(arg.to_i64) end end diff --git a/src/arg_parser/errors.cr b/src/arg_parser/errors.cr index 26680ae..993a287 100644 --- a/src/arg_parser/errors.cr +++ b/src/arg_parser/errors.cr @@ -18,8 +18,9 @@ module ArgParser end class ValidationError < Error - def initialize(name, errors) - super("Validation failed for #{name}: #{errors.join(", ")}") + def initialize(name, value, errors) + super("Validation failed for field :#{name} with value #{value.inspect}:\n" + + errors.map { |e| " - #{e}" }.join("\n")) end end