diff --git a/Cargo.lock b/Cargo.lock index 17e8d1d3..8664b0d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,6 +45,54 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +[[package]] +name = "anstream" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "anyhow" version = "1.0.79" @@ -205,12 +253,28 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bstr" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "bumpalo" version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +[[package]] +name = "bytecount" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" + [[package]] name = "byteorder" version = "1.5.0" @@ -238,6 +302,66 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", + "terminal_size", +] + +[[package]] +name = "clap_derive" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.52.0", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -284,6 +408,25 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-queue" version = "0.3.11" @@ -309,6 +452,68 @@ dependencies = [ "typenum", ] +[[package]] +name = "cucumber" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5063d8cf24f4998ad01cac265da468a15ca682a8f4f826d50e661964e8d9b8" +dependencies = [ + "anyhow", + "async-trait", + "clap", + "console", + "cucumber-codegen", + "cucumber-expressions", + "derive_more", + "drain_filter_polyfill", + "either", + "futures", + "gherkin", + "globwalk", + "humantime", + "inventory", + "itertools 0.12.0", + "lazy-regex", + "linked-hash-map", + "once_cell", + "pin-project", + "regex", + "sealed", + "serde", + "serde_json", + "smart-default", +] + +[[package]] +name = "cucumber-codegen" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01091e28d1f566c8b31b67948399d2efd6c0a8f6228a9785519ed7b73f7f0aef" +dependencies = [ + "cucumber-expressions", + "inflections", + "itertools 0.12.0", + "proc-macro2", + "quote", + "regex", + "syn 2.0.48", + "synthez", +] + +[[package]] +name = "cucumber-expressions" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d794fed319eea24246fb5f57632f7ae38d61195817b7eb659455aa5bdd7c1810" +dependencies = [ + "derive_more", + "either", + "nom", + "nom_locate", + "regex", + "regex-syntax 0.7.5", +] + [[package]] name = "der" version = "0.7.8" @@ -329,6 +534,17 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "digest" version = "0.10.7" @@ -353,6 +569,12 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "drain_filter_polyfill" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "669a445ee724c5c69b1b06fe0b63e70a1c84bc9bb7d9696cd4f4e3ec45050408" + [[package]] name = "either" version = "1.9.0" @@ -362,6 +584,12 @@ dependencies = [ "serde", ] +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "encoding_rs" version = "0.8.33" @@ -601,12 +829,53 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gherkin" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20b79820c0df536d1f3a089a2fa958f61cb96ce9e0f3f8f507f5a31179567755" +dependencies = [ + "heck", + "peg", + "quote", + "serde", + "serde_json", + "syn 2.0.48", + "textwrap", + "thiserror", + "typed-builder", +] + [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata 0.4.5", + "regex-syntax 0.8.2", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", +] + [[package]] name = "h2" version = "0.3.24" @@ -744,6 +1013,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "0.14.28" @@ -803,6 +1078,22 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "ignore" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata 0.4.5", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -823,6 +1114,18 @@ dependencies = [ "hashbrown 0.14.3", ] +[[package]] +name = "inflections" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" + +[[package]] +name = "inventory" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" + [[package]] name = "ipnet" version = "2.9.0" @@ -877,6 +1180,29 @@ dependencies = [ "simple_asn1", ] +[[package]] +name = "lazy-regex" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d12be4595afdf58bd19e4a9f4e24187da2a66700786ff660a418e9059937a4c" +dependencies = [ + "lazy-regex-proc_macros", + "once_cell", + "regex", +] + +[[package]] +name = "lazy-regex-proc_macros" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44bcd58e6c97a7fcbaffcdc95728b393b8d98933bfadad49ed4097845b57ef0b" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn 2.0.48", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -909,6 +1235,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -1039,6 +1371,17 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom_locate" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" +dependencies = [ + "bytecount", + "memchr", + "nom", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1218,6 +1561,33 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "peg" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f76678828272f177ac33b7e2ac2e3e73cc6c1cd1e3e387928aa69562fa51367" +dependencies = [ + "peg-macros", + "peg-runtime", +] + +[[package]] +name = "peg-macros" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "636d60acf97633e48d266d7415a9355d4389cea327a193f87df395d88cd2b14d" +dependencies = [ + "peg-runtime", + "proc-macro2", + "quote", +] + +[[package]] +name = "peg-runtime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555b1514d2d99d78150d3c799d4c357a3e2c2a8062cd108e93a06d9057629c5" + [[package]] name = "pem" version = "3.0.3" @@ -1487,6 +1857,7 @@ dependencies = [ name = "ratings" version = "0.2.0" dependencies = [ + "cucumber", "dotenv", "envy", "futures", @@ -1560,6 +1931,12 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + [[package]] name = "regex-syntax" version = "0.8.2" @@ -1701,6 +2078,15 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.23" @@ -1726,6 +2112,18 @@ dependencies = [ "untrusted", ] +[[package]] +name = "sealed" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a8caec23b7800fb97971a1c6ae365b6239aaeddfb934d6265f8505e795699d" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "security-framework" version = "2.9.2" @@ -1875,6 +2273,23 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +[[package]] +name = "smart-default" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + [[package]] name = "socket2" version = "0.5.5" @@ -2136,6 +2551,12 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + [[package]] name = "subtle" version = "2.5.0" @@ -2170,6 +2591,39 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "synthez" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d2c2202510a1e186e63e596d9318c91a8cbe85cd1a56a7be0c333e5f59ec8d" +dependencies = [ + "syn 2.0.48", + "synthez-codegen", + "synthez-core", +] + +[[package]] +name = "synthez-codegen" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f724aa6d44b7162f3158a57bccd871a77b39a4aef737e01bcdff41f4772c7746" +dependencies = [ + "syn 2.0.48", + "synthez-core", +] + +[[package]] +name = "synthez-core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bfa6ec52465e2425fd43ce5bbbe0f0b623964f7c63feb6b10980e816c654ea" +dependencies = [ + "proc-macro2", + "quote", + "sealed", + "syn 2.0.48", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -2204,6 +2658,27 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "terminal_size" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +dependencies = [ + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "textwrap" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] + [[package]] name = "thiserror" version = "1.0.57" @@ -2534,6 +3009,26 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typed-builder" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe83c85a85875e8c4cb9ce4a890f05b23d38cd0d47647db7895d3d2a79566d2" +dependencies = [ + "typed-builder-macro", +] + +[[package]] +name = "typed-builder-macro" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29a3151c41d0b13e3d011f98adc24434560ef06673a155a6c7f66b9879eecce2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "typenum" version = "1.17.0" @@ -2552,6 +3047,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + [[package]] name = "unicode-normalization" version = "0.1.22" @@ -2567,6 +3068,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + [[package]] name = "unicode_categories" version = "0.1.1" @@ -2596,6 +3103,12 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "valuable" version = "0.1.0" @@ -2614,6 +3127,16 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -2749,6 +3272,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 97336843..ecb76909 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,11 @@ rand = "0.8" reqwest = "0.11" serde = { version = "1.0", features = ["derive"] } sha2 = "0.10" -sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "time"] } +sqlx = { version = "0.7", features = [ + "runtime-tokio-rustls", + "postgres", + "time", +] } thiserror = "1.0" time = { version = "0.3", features = ["macros"] } tokio = { version = "1.36", features = ["full"] } @@ -34,3 +38,10 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } [build-dependencies] tonic-build = { version = "0.11", features = ["prost"] } + +[dev-dependencies] +cucumber = { version = "0.20.2", features = ["libtest"] } + +[[test]] +name = "voting" +harness = false diff --git a/src/features/mod.rs b/src/features/mod.rs index 1111ba07..95328d27 100644 --- a/src/features/mod.rs +++ b/src/features/mod.rs @@ -5,4 +5,4 @@ pub mod rating; pub mod user; mod common; -mod pb; +pub mod pb; diff --git a/tests/features/voting.feature b/tests/features/voting.feature new file mode 100644 index 00000000..294a9810 --- /dev/null +++ b/tests/features/voting.feature @@ -0,0 +1,22 @@ +Feature: User voting + Background: + Given a Snap named "chu-chu-garden" has already accumulated 5 votes and 3 upvotes + + Scenario: Amy upvotes a snap she hasn't voted for in the past + When Amy casts an upvote + Then the total number of votes increases + And the total number of upvotes increases + + Rule: Votes that a user updates do not change the total vote count + + Scenario Outline: Sonic changes his vote between downvote and upvote because "chu-chu-garden" got better/worse + Given Sonic originally voted + When Sonic changes his vote to + Then the total number of of upvotes + But the total number of votes stays constant + + Examples: + | original | after | direction | + | upvote | downvote | decreases | + | downvote | upvote | increases | + diff --git a/tests/helpers/client_app.rs b/tests/helpers/client_app.rs index e29aeffe..89dec49f 100644 --- a/tests/helpers/client_app.rs +++ b/tests/helpers/client_app.rs @@ -1,8 +1,11 @@ +use futures::task::noop_waker; use tonic::{metadata::MetadataValue, transport::Endpoint, Request, Response, Status}; -use crate::pb::app::{GetRatingRequest, GetRatingResponse}; +use ratings::features::pb::app::{GetRatingRequest, GetRatingResponse}; -use crate::pb::app::app_client as pb; +use ratings::features::pb::app::app_client as pb; + +pub trait AppClient {} #[derive(Debug, Clone)] pub struct AppClient { @@ -25,6 +28,7 @@ impl AppClient { .unwrap() .connect() .await + .noop_waker() .unwrap(); let mut client = pb::AppClient::with_interceptor(channel, move |mut req: Request<()>| { let header: MetadataValue<_> = format!("Bearer {token}").parse().unwrap(); diff --git a/tests/helpers/client_chart.rs b/tests/helpers/client_chart.rs index 4e952880..8d042096 100644 --- a/tests/helpers/client_chart.rs +++ b/tests/helpers/client_chart.rs @@ -2,8 +2,8 @@ use tonic::metadata::MetadataValue; use tonic::transport::Endpoint; use tonic::{Request, Response, Status}; -use crate::pb::chart::{chart_client as pb, Timeframe}; -use crate::pb::chart::{GetChartRequest, GetChartResponse}; +use ratings::features::pb::chart::{chart_client as pb, Timeframe}; +use ratings::features::pb::chart::{GetChartRequest, GetChartResponse}; #[derive(Debug, Clone)] pub struct ChartClient { diff --git a/tests/helpers/client_user.rs b/tests/helpers/client_user.rs index c967f2a0..b338d1dc 100644 --- a/tests/helpers/client_user.rs +++ b/tests/helpers/client_user.rs @@ -2,8 +2,8 @@ use tonic::metadata::MetadataValue; use tonic::transport::Endpoint; use tonic::{Request, Response, Status}; -use crate::pb::user::user_client as pb; -use crate::pb::user::{ +use ratings::features::pb::user::user_client as pb; +use ratings::features::pb::user::{ AuthenticateRequest, AuthenticateResponse, GetSnapVotesRequest, GetSnapVotesResponse, VoteRequest, }; diff --git a/tests/helpers/test_client.rs b/tests/helpers/test_client.rs new file mode 100644 index 00000000..e69de29b diff --git a/tests/helpers/vote_generator.rs b/tests/helpers/vote_generator.rs index 0ad012c5..f137af58 100644 --- a/tests/helpers/vote_generator.rs +++ b/tests/helpers/vote_generator.rs @@ -1,16 +1,17 @@ -use super::test_data::TestData; +use super::{client_user::UserClient, test_data::TestData}; use crate::helpers; -use crate::pb::user::{AuthenticateResponse, VoteRequest}; +use cucumber::cli; +use ratings::features::pb::user::{AuthenticateResponse, VoteRequest}; pub async fn generate_votes( snap_id: &str, snap_revision: i32, vote_up: bool, count: u64, - data: TestData, + client: &UserClient, ) -> Result<(), Box> { for _ in 0..count { - register_and_vote(snap_id, snap_revision, vote_up, data.clone()).await?; + register_and_vote(snap_id, snap_revision, vote_up, client).await?; } Ok(()) } @@ -19,9 +20,8 @@ async fn register_and_vote( snap_id: &str, snap_revision: i32, vote_up: bool, - data: TestData, + client: &UserClient, ) -> Result<(), Box> { - let client = data.user_client.clone().unwrap(); let id: String = helpers::data_faker::rnd_sha_256(); let response: AuthenticateResponse = client .authenticate(&id) diff --git a/tests/voting.rs b/tests/voting.rs new file mode 100644 index 00000000..1fcf04cc --- /dev/null +++ b/tests/voting.rs @@ -0,0 +1,215 @@ +use std::str::FromStr; + +use cucumber::{given, then, when, Parameter, World}; +use helpers::client_user::UserClient; +use ratings::{ + features::pb::user::{GetSnapVotesRequest, VoteRequest}, + utils::Config, +}; + +mod helpers; + +#[derive(Debug, Default)] +struct AuthenticatedUser { + token: String, +} + +#[derive(Debug, Default, Copy, Clone, Parameter)] +#[param(name = "vote-type", regex = "upvote|downvote")] +enum VoteType { + #[default] + Upvote, + Downvote, +} + +impl FromStr for VoteType { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(match s { + "upvote" => Self::Upvote, + "downvote" => Self::Downvote, + _ => return Err(format!("invalid vote type {s}")), + }) + } +} + +impl From for bool { + fn from(value: VoteType) -> Self { + match value { + VoteType::Upvote => true, + VoteType::Downvote => false, + } + } +} + +impl From for u64 { + fn from(value: VoteType) -> Self { + bool::from(value) as u64 + } +} + +#[derive(Debug, Default, Copy, Clone, Parameter)] +#[param(name = "direction", regex = "increases|decreases|stays constant")] +enum Direction { + #[default] + Increase, + Decrease, + StaysConstant, +} + +impl FromStr for Direction { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(match s { + "increases" => Self::Increase, + "decreases" => Self::Decrease, + "stays constant" => Self::StaysConstant, + _ => return Err(format!("invalid vote count direction {s}")), + }) + } +} + +impl Direction { + fn check_and_apply(&self, current: &mut u64, new: u64) { + match self { + Direction::Decrease => { + assert_eq!(new, *current - 1); + *current -= 1; + } + Direction::Increase => { + assert_eq!(new, *current + 1); + *current += 1; + } + Direction::StaysConstant => assert_eq!(new, *current), + }; + } +} + +#[derive(Debug, PartialEq, Eq)] +struct Snap(String); + +impl Default for Snap { + fn default() -> Self { + Snap("93jv9vhsfbb8f7".to_string()) + } +} + +#[derive(Debug, World)] +#[world(init = Self::new)] +struct VotingWorld { + user: AuthenticatedUser, + client: UserClient, + snap: Snap, + votes: u64, + upvotes: u64, +} + +impl VotingWorld { + async fn new() -> Self { + let config = Config::load().expect("Could not load config"); + let client = UserClient::new(&config.socket()); + + let id = helpers::data_faker::rnd_sha_256(); + let user = AuthenticatedUser { + token: client + .authenticate(&id) + .await + .expect("could not authenticate user") + .into_inner() + .token, + }; + + VotingWorld { + user, + client, + snap: Default::default(), + votes: 0, + upvotes: 0, + } + } +} + +#[given(expr = "a Snap named {string} has already accumulated {int} votes and {int} upvotes")] +async fn seed_snap(world: &mut VotingWorld, _snap_name: String, votes: u64, upvotes: u64) { + world.snap.0 = helpers::data_faker::rnd_id(); + helpers::vote_generator::generate_votes(&world.snap.0, 1, true, upvotes, &world.client) + .await + .expect("could not generate votes"); + helpers::vote_generator::generate_votes( + &world.snap.0, + 1, + false, + votes - upvotes, + &world.client, + ) + .await + .expect("could not generate votes"); + + world.votes = votes; + world.upvotes = upvotes; +} + +#[when(expr = "{word} casts a(n) {vote-type}")] +#[given(expr = "{word} originally voted {vote-type}")] +#[then(expr = "{word} changes his/her/their vote to {vote-type}")] +async fn vote(world: &mut VotingWorld, _user_name: String, vote_type: VoteType) { + let request = VoteRequest { + snap_id: world.snap.0.clone(), + snap_revision: 1, + vote_up: vote_type.into(), + }; + + world + .client + .vote(&world.user.token, request) + .await + .expect("could not cast vote"); +} + +#[then(expr = "the total number of votes {direction}")] +async fn check_vote(world: &mut VotingWorld, direction: Direction) { + let votes = world + .client + .get_snap_votes( + &world.user.token, + GetSnapVotesRequest { + snap_id: world.snap.0.clone(), + }, + ) + .await + .expect("could not get snap votes") + .into_inner(); + panic!("{:?}", votes); + + let votes = votes.votes.len(); + + direction.check_and_apply(&mut world.votes, votes as u64); +} + +#[then(expr = "The total number of upvotes {direction}")] +async fn check_upvote(world: &mut VotingWorld, direction: Direction) { + let upvotes: u64 = world + .client + .get_snap_votes( + &world.user.token, + GetSnapVotesRequest { + snap_id: world.snap.0.clone(), + }, + ) + .await + .expect("could not get snap votes") + .into_inner() + .votes + .into_iter() + .map(|v| v.vote_up as u64) + .sum(); + + direction.check_and_apply(&mut world.upvotes, upvotes as u64); +} + +#[tokio::main] +async fn main() { + VotingWorld::run("tests/features/voting.feature").await; +}