Skip to content

Commit

Permalink
Merge pull request #9 from y2k2mt/feature/compare-request-by-score
Browse files Browse the repository at this point in the history
Compare request by score
  • Loading branch information
y2k2mt authored Dec 5, 2022
2 parents df46c38 + cede356 commit 9880c7a
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 43 deletions.
14 changes: 14 additions & 0 deletions .ameba.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# This configuration file was generated by `ameba --gen-config`
# on 2022-12-03 11:45:30 UTC using Ameba version 1.3.1.
# The point is for the user to remove these configuration records
# one by one as the reported problems are removed from the code base.

# Problems found: 1
# Run `ameba --only Metrics/CyclomaticComplexity` for details
Metrics/CyclomaticComplexity:
Description: Disallows methods with a cyclomatic complexity higher than `MaxComplexity`
MaxComplexity: 15
Excluded:
- src/replay/http/request.cr
Enabled: true
Severity: Convention
91 changes: 86 additions & 5 deletions spec/replay/http/request_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,56 @@ describe HTTPRequest do
actual_metadatas["indexed"]["body"].should eq("")
actual_metadatas["not_indexed"]["body"].should eq("HELLO")
end

it "can compare json proeprties" do
headers = HTTP::Headers{
"Content-Type" => "application/json",
}
json = %q{
{
"foo":"bar",
"baz":"qux",
"hoge": {
"fuga": 1,
"moge":"bar",
"baz" : [1,3,2]
}
}
}

another_json = %q{
{
"hoge": {
"fuga": 1
}
}
}
request = HTTP::Request.new("POST", "/hello", headers, json)
request1 = HTTPRequest.new(request, URI.parse "http://base.uri")
request2 = HTTPRequest.new(id: "foo", base_uri: URI.parse("http://base.uri"), path: "/hello", method: "POST", headers: headers.to_h, body: another_json, params: {} of String => String)
(request2.score(request1)).should eq 1
end

it "can get multiple properties via server request" do
headers = HTTP::Headers{"Content-Type" => "text/plain"}
request = HTTP::Request.new("POST", "/hello", headers, "HELLO")
actual = HTTPRequest.new(request, URI.parse "http://base.uri")
# Write to response
actual.host_name.should eq("base.uri")
actual.path.should eq("/hello")
actual.method.should eq("POST")
actual.body.should eq("HELLO")
actual.body.should eq("HELLO")
actual.headers["Content-Type"][0].should eq("text/plain")
actual.base_index.should eq("ce3e48c460a779f1554cd6e845a5fadf8e2c9f3b5126c65207294508e2592f6e")
actual_metadatas = actual.metadatas
actual_metadatas["host"].should eq("base.uri")
actual_metadatas["method"].should eq("POST")
actual_metadatas["path"].should eq("/hello")
actual_metadatas["indexed"]["body"].should eq("")
actual_metadatas["not_indexed"]["body"].should eq("HELLO")
end

it "can compare json proeprties" do
headers = HTTP::Headers{
"Content-Type" => "application/json",
Expand Down Expand Up @@ -47,8 +97,38 @@ describe HTTPRequest do
request = HTTP::Request.new("POST", "/hello", headers, json)
request1 = HTTPRequest.new(request, URI.parse "http://base.uri")
request2 = HTTPRequest.new(id: "foo", base_uri: URI.parse("http://base.uri"), path: "/hello", method: "POST", headers: headers.to_h, body: another_json, params: {} of String => String)
(request2 == request1).should be_true
(request2.score(request1)).should eq 2
end

it "can compare json proeprties" do
headers = HTTP::Headers{
"Content-Type" => "application/json",
}
json = %q{
{
"foo":"bar",
"baz":"qux",
"hoge": {
"fuga": 1,
"moge":"bar",
"baz" : [1,3,2]
}
}
}

another_json = %q{
{
"hoge": {
"fuga": 1
}
}
}
request = HTTP::Request.new("POST", "/hello", headers, json)
request1 = HTTPRequest.new(request, URI.parse "http://base.uri")
request2 = HTTPRequest.new(id: "foo", base_uri: URI.parse("http://base.uri"), path: "/bye", method: "POST", headers: headers.to_h, body: another_json, params: {} of String => String)
(request2.score(request1)).should eq -1
end

it "can not compare json proeprties" do
headers = HTTP::Headers{
"Content-Type" => "application/json",
Expand Down Expand Up @@ -76,8 +156,9 @@ describe HTTPRequest do
request = HTTP::Request.new("POST", "/hello", headers, json)
request1 = HTTPRequest.new(request, URI.parse "http://base.uri")
request2 = HTTPRequest.new(id: "foo", base_uri: URI.parse("http://base.uri"), path: "/hello", method: "POST", headers: headers.to_h, body: another_json, params: {} of String => String)
(request2 == request1).should be_false
(request2.score(request1)).should eq -1
end

it "can compare form proeprties" do
headers = HTTP::Headers{
"Content-Type" => "application/x-www-form-urlencoded",
Expand All @@ -88,7 +169,7 @@ describe HTTPRequest do
request = HTTP::Request.new("POST", "/hello", headers, form)
request1 = HTTPRequest.new(request, URI.parse "http://base.uri")
request2 = HTTPRequest.new(id: "foo", base_uri: URI.parse("http://base.uri"), path: "/hello", method: "POST", headers: headers.to_h, body: another_form, params: {} of String => String)
(request2 == request1).should be_true
(request2.score(request1)).should eq 2
end
it "can not compare form proeprties" do
headers = HTTP::Headers{
Expand All @@ -98,7 +179,7 @@ describe HTTPRequest do
another_form = "foo=baz&fuga=1"
request = HTTP::Request.new("POST", "/hello", headers, form)
request1 = HTTPRequest.new(request, URI.parse "http://base.uri")
request2 = HTTPRequest.new(id: "foo", base_uri: URI.parse("http://base.uri"), path: "/hello", method: "POST", headers: headers.to_h, body: another_form, params: {} of String => String)
(request2 == request1).should be_false
request2 = HTTPRequest.new(id: "foo", base_uri: URI.parse("http://base.uri"), path: "/hello_foo", method: "POST", headers: headers.to_h, body: another_form, params: {} of String => String)
(request2.score(request1)).should eq -1
end
end
8 changes: 6 additions & 2 deletions spec/support/mocks.cr
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,12 @@ class MockRequest
@base_index
end

def ==(other : Request)
self.hash == other.hash
def score(other : Request) : Int32
if self.hash == other.hash
1
else
-1
end
end

def proxy
Expand Down
2 changes: 1 addition & 1 deletion src/replay.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ require "./cli"

# TODO: Write documentation for `Replay`
module Replay
VERSION = "0.2.0"
VERSION = "0.2.4"
end
16 changes: 11 additions & 5 deletions src/replay/datasource/filesystem.cr
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,18 @@ class FileSystemDatasource
Replay::Log.debug { "No index_file avairable." }
NoIndexFound.new(meta_index)
else
found_index_file = index_files.find do |index_file|
Replay::Log.debug { "#{index_files.size} index_file are avairable." }
most_matched_index_file_path : String? = index_files.map do |index_file|
Replay::Log.debug { "Parsing: #{index_file}" }
candidate = @requests.from(JSON.parse(File.read(index_file)))
candidate == request
end
found_index_file.try do |found|
Replay::Log.debug { "Found index_file path: #{found}" }
scored = candidate.score(request)
Replay::Log.debug { "Score: #{scored} for #{index_file}" }
{scored, index_file}
end.max_by? do |t|
t[0]
end.try &.[1]
most_matched_index_file_path.try do |found|
Replay::Log.debug { "Most matched index_file path: #{found}" }
found_index = @requests.from(JSON.parse(File.read(found)))
body_file = Dir["#{@reply_file_dir}/#{found_index.id_index}"].first?
header_file = Dir["#{@reply_file_dir}/#{found_index.id_index}_headers"].first?
Expand Down
4 changes: 2 additions & 2 deletions src/replay/errors.cr
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ struct NoResourceFound
end

struct CorruptedReplayResource
getter index
getter message, index

def initialize(@index : String)
def initialize(@message : String, @index : String? = nil)
end
end
2 changes: 1 addition & 1 deletion src/replay/http/errors.cr
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class HTTPErrorHandler
message = "Not recorded yet : No resource found : #{error.index}"
when CorruptedReplayResource
response.status_code = 500
message = "Broken resource : #{error.index}"
message = "Broken resource : #{error.message} -> #{error.index}"
else
response.status_code = 500
end
Expand Down
66 changes: 40 additions & 26 deletions src/replay/http/request.cr
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,24 @@ class HTTPRequest
"#{base_index}_#{@id}"
}

def ==(other : Request) : Bool
def score(other : Request) : Int32
Replay::Log.debug { "Comparing : #{self.base_index} and #{other.base_index}." }
case other
when HTTPRequest
# TODO: Plaggable comparators
other.base_index == self.base_index &&
match_headers(self, other) &&
(self.body.empty? || self.body == other.body || match_json(self, other) || match_form(self, other))
if (other.base_index != self.base_index || !match_headers(self, other))
-1
else
# TODO: Plaggable comparators
if self.body.empty?
0
elsif self.body == other.body
1024
else
match_json(self, other) + match_form(self, other)
end
end
else
false
-1
end
end

Expand All @@ -90,46 +98,52 @@ class HTTPRequest
(i.params.empty? || i.params.find { |k, v| !other.params[k] || other.params[k] != v } == nil)
end

private def match_json(i : Request, other : Request) : Bool
private def match_json(i : Request, other : Request) : Int32
me = JSON.parse i.body
another = JSON.parse other.body
match_json_internal me, another
rescue e : JSON::ParseException
false
0
end

private def match_json_internal(me : JSON::Any, other : JSON::Any) : Bool
me.as_h.keys.find do |key|
private def match_json_internal(me : JSON::Any, other : JSON::Any) : Int32
me.as_h.keys.reduce(0) do |acc, key|
case value = other[key]
when .as_s?
value != me[key].as_s?
value == me[key].as_s? ? acc + 1 : return -1
when .as_i?
value != me[key].as_i?
value == me[key].as_i? ? acc + 1 : return -1
when .as_bool?
value != me[key].as_bool?
value == me[key].as_bool? ? acc + 1 : return -1
when .as_a?
value != me[key].as_a?
value == me[key].as_a? ? acc + 1 : return -1
when .as_f?
value != me[key].as_f?
value == me[key].as_f? ? acc + 1 : return -1
when .as_h?
me[key].as_h?.try do |_|
match_json_internal me[key], value
end ? nil : value
child = match_json_internal me[key], value
child == -1 ? return -1 : child + acc
end || acc
else
acc
end
end == nil
end || 0
end

private def match_form(i : Request, other : Request) : Bool
private def match_form(i : Request, other : Request) : Int32
me = split_form(i.body)
another = split_form(other.body)
me.keys.find do |k|
!another.keys.includes?(k)
end == nil &&
me.find do |k, v|
another[k]?.try do |a|
v == nil || a != v
begin
me.keys.reduce(0) do |acc, key|
if me[key] == another[key]
acc + 1
else
return -1
end
end == nil
end
rescue e : KeyError
0
end
end

private def split_form(body)
Expand Down
2 changes: 1 addition & 1 deletion src/replay/request.cr
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module Request
def base_index : String
end

def ==(other : Request) : Bool
def score(other : Request) : Int32
end

def proxy
Expand Down

0 comments on commit 9880c7a

Please sign in to comment.