Skip to content

Commit fe04975

Browse files
committed
feat: add v3/v4 generators (thanks @slt)
1 parent 7a09875 commit fe04975

26 files changed

+715
-0
lines changed

README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,48 @@
33
![Build status](https://github.com/pact-foundation/pact-support/workflows/Test/badge.svg)
44

55
Provides shared code for the Pact gems
6+
7+
## Supported matching rules
8+
9+
| matcher | Spec Version | Implemented | Usage|
10+
|---------------|--------------|-------------|-------------|
11+
| Equality | V1 | | |
12+
| Regex | V2 || `Pact.term(generate, matcher)` |
13+
| Type | V2 || `Pact.like(generate)` |
14+
| MinType | V2 || `Pact.each_like(generate, min: <val>)` |
15+
| MaxType | V2 | | |
16+
| MinMaxType | V2 | | |
17+
| Include | V3 | | |
18+
| Integer | V3 | | |
19+
| Decimal | V3 | | |
20+
| Number | V3 | | |
21+
| Timestamp | V3 | | |
22+
| Time | V3 | | |
23+
| Date | V3 | | |
24+
| Null | V3 | | |
25+
| Boolean | V3 | | |
26+
| ContentType | V3 | | |
27+
| Values | V3 | | |
28+
| ArrayContains | V4 | | |
29+
| StatusCode | V4 | | |
30+
| NotEmpty | V4 | | |
31+
| Semver | V4 | | |
32+
| EachKey | V4 | | |
33+
| EachValue | V4 | | |
34+
35+
## Supported generators
36+
37+
| matcher | Spec Version | Implemented |
38+
|------------------------|--------------|----|
39+
| RandomInt | V3 ||
40+
| RandomDecimal | V3 ||
41+
| RandomHexadecimal | V3 ||
42+
| RandomString | V3 ||
43+
| Regex | V3 ||
44+
| Uuid | V3/V4 ||
45+
| Date | V3 ||
46+
| Time | V3 ||
47+
| DateTime | V3 ||
48+
| RandomBoolean | V3 ||
49+
| ProviderState | V4 ||
50+
| MockServerURL | V4 | 🚧 |

lib/pact/generator/date.rb

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
require 'date'
2+
3+
module Pact
4+
module Generator
5+
# Date provides the time generator which will give the current date in the defined format
6+
class Date
7+
def can_generate?(hash)
8+
hash.key?('type') && hash['type'] == type
9+
end
10+
11+
def call(hash, _params = nil, _example_value = nil)
12+
format = hash['format'] || default_format
13+
::Time.now.strftime(convert_from_java_simple_date_format(format))
14+
end
15+
16+
def type
17+
'Date'
18+
end
19+
20+
def default_format
21+
'yyyy-MM-dd'
22+
end
23+
24+
# Format for the pact specficiation should be the Java DateTimeFormmater
25+
# This tries to convert to something Ruby can format.
26+
def convert_from_java_simple_date_format(format)
27+
# Year
28+
format.sub!(/(?<!%)y{4,}/, '%Y')
29+
format.sub!(/(?<!%)y{1,}/, '%y')
30+
31+
# Month
32+
format.sub!(/(?<!%)M{4,}/, '%B')
33+
format.sub!(/(?<!%)M{3}/, '%b')
34+
format.sub!(/(?<!%)M{1,2}/, '%m')
35+
36+
# Week
37+
format.sub!(/(?<!%)M{1,}/, '%W')
38+
39+
# Day
40+
format.sub!(/(?<!%)D{1,}/, '%j')
41+
format.sub!(/(?<!%)d{1,}/, '%d')
42+
format.sub!(/(?<!%)E{4,}/, '%A')
43+
format.sub!(/(?<!%)D{1,}/, '%a')
44+
format.sub!(/(?<!%)u{1,}/, '%u')
45+
46+
# Time
47+
format.sub!(/(?<!%)a{1,}/, '%p')
48+
format.sub!(/(?<!%)k{1,}/, '%H')
49+
format.sub!(/(?<!%)n{1,}/, '%M')
50+
format.sub!(/(?<!%)s{1,}/, '%S')
51+
format.sub!(/(?<!%)S{1,}/, '%L')
52+
53+
# Timezone
54+
format.sub!(/(?<!%)z{1,}/, '%z')
55+
format.sub!(/(?<!%)Z{1,}/, '%z')
56+
format.sub!(/(?<!%)X{1,}/, '%Z')
57+
58+
format
59+
end
60+
end
61+
end
62+
end

lib/pact/generator/datetime.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
require 'date'
2+
3+
module Pact
4+
module Generator
5+
# DateTime provides the time generator which will give the current date time in the defined format
6+
class DateTime < Date
7+
def type
8+
'DateTime'
9+
end
10+
11+
def default_format
12+
'yyyy-MM-dd HH:mm'
13+
end
14+
end
15+
end
16+
end
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
require 'pact/logging'
2+
3+
module Pact
4+
module Generator
5+
# ProviderState provides the provider state generator which will inject
6+
# values provided by the provider state setup url.
7+
class ProviderState
8+
include Pact::Logging
9+
10+
# rewrite of https://github.com/DiUS/pact-jvm/blob/master/core/support/src/main/kotlin/au/com/dius/pact/core/support/expressions/ExpressionParser.kt#L27
11+
VALUES_SEPARATOR = ','
12+
START_EXPRESSION = "\${"
13+
END_EXPRESSION = '}'
14+
15+
def can_generate?(hash)
16+
hash.key?('type') && hash['type'] == 'ProviderState'
17+
end
18+
19+
def call(hash, params = nil, _example_value = nil)
20+
params ||= {}
21+
parse_expression hash['expression'], params
22+
end
23+
24+
def parse_expression(expression, params)
25+
return_string = []
26+
buffer = expression
27+
# initial value
28+
position = buffer.index(START_EXPRESSION)
29+
30+
while position && position >= 0
31+
if position.positive?
32+
# add string
33+
return_string.push(buffer[0...position])
34+
end
35+
end_position = buffer.index(END_EXPRESSION, position)
36+
raise 'Missing closing brace in expression string' if !end_position || end_position.negative?
37+
38+
variable = buffer[position + 2...end_position]
39+
40+
logger.info "Could not subsitute provider state key #{variable}, have #{params}" unless params[variable]
41+
42+
expression = params[variable] || ''
43+
return_string.push(expression)
44+
45+
buffer = buffer[end_position + 1...-1]
46+
position = buffer.index(START_EXPRESSION)
47+
end
48+
49+
return_string.join('')
50+
end
51+
end
52+
end
53+
end
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module Pact
2+
module Generator
3+
# Boolean provides the boolean generator which will give a true or false value
4+
class RandomBoolean
5+
def can_generate?(hash)
6+
hash.key?('type') && hash['type'] == 'RandomBoolean'
7+
end
8+
9+
def call(_hash, _params = nil, _example_value = nil)
10+
[true, false].sample
11+
end
12+
end
13+
end
14+
end
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
require 'bigdecimal'
2+
3+
module Pact
4+
module Generator
5+
# RandomDecimal provides the random decimal generator which will generate a decimal value of digits length
6+
class RandomDecimal
7+
def can_generate?(hash)
8+
hash.key?('type') && hash['type'] == 'RandomDecimal'
9+
end
10+
11+
def call(hash, _params = nil, _example_value = nil)
12+
digits = hash['digits'] || 6
13+
14+
raise 'RandomDecimalGenerator digits must be > 0, got $digits' if digits < 1
15+
16+
return rand(0..9) if digits == 1
17+
18+
return rand(0..9) + rand(1..9) / 10 if digits == 2
19+
20+
pos = rand(1..digits - 1)
21+
precision = digits - pos
22+
integers = ''
23+
decimals = ''
24+
while pos.positive?
25+
integers += String(rand(1..9))
26+
pos -= 1
27+
end
28+
while precision.positive?
29+
decimals += String(rand(1..9))
30+
precision -= 1
31+
end
32+
33+
Float("#{integers}.#{decimals}")
34+
end
35+
end
36+
end
37+
end
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
require 'securerandom'
2+
3+
module Pact
4+
module Generator
5+
# RandomHexadecimal provides the random hexadecimal generator which will generate a hexadecimal
6+
class RandomHexadecimal
7+
def can_generate?(hash)
8+
hash.key?('type') && hash['type'] == 'RandomHexadecimal'
9+
end
10+
11+
def call(hash, _params = nil, _example_value = nil)
12+
digits = hash['digits'] || 8
13+
bytes = (digits / 2).ceil
14+
string = SecureRandom.hex(bytes)
15+
string[0, digits]
16+
end
17+
end
18+
end
19+
end

lib/pact/generator/random_int.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module Pact
2+
module Generator
3+
# RandomInt provides the random int generator which generate a random integer, with a min/max
4+
class RandomInt
5+
def can_generate?(hash)
6+
hash.key?('type') && hash['type'] == 'RandomInt'
7+
end
8+
9+
def call(hash, _params = nil, _example_value = nil)
10+
min = hash['min'] || 0
11+
max = hash['max'] || 2_147_483_647
12+
rand(min..max)
13+
end
14+
end
15+
end
16+
end
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module Pact
2+
module Generator
3+
# RandomString provides the random string generator which generate a random string of size length
4+
class RandomString
5+
def can_generate?(hash)
6+
hash.key?('type') && hash['type'] == 'RandomString'
7+
end
8+
9+
def call(hash, _params = nil, _example_value = nil)
10+
size = hash['size'] || 20
11+
string = rand(36**(size + 2)).to_s(36)
12+
string[0, size]
13+
end
14+
end
15+
end
16+
end

lib/pact/generator/regex.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
require 'string_pattern'
2+
3+
module Pact
4+
module Generator
5+
# Regex provides the regex generator which will generate a value based on the regex pattern provided
6+
class Regex
7+
def can_generate?(hash)
8+
hash.key?('type') && hash['type'] == 'Regex'
9+
end
10+
11+
def call(hash, _params = nil, _example_value = nil)
12+
pattern = hash['pattern'] || ''
13+
StringPattern.generate(Regexp.new(pattern))
14+
end
15+
end
16+
end
17+
end

0 commit comments

Comments
 (0)