From 606e727d27d25007a0c211d31f1715df0d3fcf60 Mon Sep 17 00:00:00 2001 From: DevForge Engineer Date: Mon, 18 May 2026 04:29:25 -0400 Subject: [PATCH] test(cli): add CLI integration tests, fix pages.yml checkout, update README tool count and URLs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Cherry-pick 18 CLI tests from stale Openclaw branch (coverage 73%→89%) - Fix pages.yml: actions/checkout@v4 → v6 for consistency - Fix README: 'eight tools' → 'eleven CLI tools', 'all 8 tools' → 'all 11 CLI tools' - Fix README footer URL: devforge.dev → revenueholdings.dev --- .github/workflows/pages.yml | 2 +- README.md | 6 +- .../__pycache__/__init__.cpython-312.pyc | Bin 269 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 271 -> 0 bytes src/json2sql/__pycache__/cli.cpython-312.pyc | Bin 3854 -> 0 bytes src/json2sql/__pycache__/cli.cpython-314.pyc | Bin 4110 -> 0 bytes .../__pycache__/converter.cpython-312.pyc | Bin 9957 -> 0 bytes .../__pycache__/converter.cpython-314.pyc | Bin 12208 -> 0 bytes .../__pycache__/dialects.cpython-312.pyc | Bin 4736 -> 0 bytes .../__pycache__/dialects.cpython-314.pyc | Bin 5821 -> 0 bytes ...est_converter.cpython-312-pytest-9.0.3.pyc | Bin 24462 -> 0 bytes ...est_converter.cpython-314-pytest-9.0.3.pyc | Bin 27079 -> 0 bytes tests/test_cli.py | 183 ++++++++++++++++++ 13 files changed, 187 insertions(+), 4 deletions(-) delete mode 100644 src/json2sql/__pycache__/__init__.cpython-312.pyc delete mode 100644 src/json2sql/__pycache__/__init__.cpython-314.pyc delete mode 100644 src/json2sql/__pycache__/cli.cpython-312.pyc delete mode 100644 src/json2sql/__pycache__/cli.cpython-314.pyc delete mode 100644 src/json2sql/__pycache__/converter.cpython-312.pyc delete mode 100644 src/json2sql/__pycache__/converter.cpython-314.pyc delete mode 100644 src/json2sql/__pycache__/dialects.cpython-312.pyc delete mode 100644 src/json2sql/__pycache__/dialects.cpython-314.pyc delete mode 100644 tests/__pycache__/test_converter.cpython-312-pytest-9.0.3.pyc delete mode 100644 tests/__pycache__/test_converter.cpython-314-pytest-9.0.3.pyc create mode 100644 tests/test_cli.py diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index d2d8ccb..29402d3 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -18,7 +18,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Setup Pages uses: actions/configure-pages@v5 - name: Build with Jekyll diff --git a/README.md b/README.md index 144dce2..21dc65a 100644 --- a/README.md +++ b/README.md @@ -87,13 +87,13 @@ sqlite3 test.db < seed.sql ## Pricing -json2sql is one of eight tools in the DevForge suite. One license covers all CLI tools. +json2sql is one of eleven CLI tools in the Revenue Holdings suite. One license covers all CLI tools. | Plan | Price | Best For | |------|-------|----------| | **Free** | $0 | Individual devs, OSS — CLI only, limited rows | | **json2sql Individual** | **$9/mo** ($7 billed annually) | Professional devs — unlimited rows, batch processing | -| **Suite (all 8 tools)** | **$49/mo** ($39 billed annually) | Full DevForge toolkit — 40% savings | +| **Suite (all 11 CLI tools)** | **$49/mo** ($39 billed annually) | Full Revenue Holdings toolkit — 40% savings | | **Team** | **$79/mo** ($63 billed annually) | Up to 5 devs — API access, CI/CD integration, priority support | | **Enterprise** | Custom | SSO, RBAC, compliance reports, dedicated support | @@ -117,7 +117,7 @@ json2sql is one of eight tools in the DevForge suite. One license covers all CLI ---

- Part of DevForge — CLI tools built by autonomous AI. + Part of Revenue Holdings — CLI tools built by autonomous AI.

## License diff --git a/src/json2sql/__pycache__/__init__.cpython-312.pyc b/src/json2sql/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index d74d10673fac5b748579e29b7df1f86529ff6d90..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 269 zcmX@j%ge<81XhPwvm$`>V-N=h7@>^MJV3^Dh7^Vg_3-Q;6NV*PrqQ-pb&-PlEjkK+|<01V!bL>13g1M z13yisTkP@iDf!9q@wd3+Qa}An}2jk&*ExgY5%8(I)O9R-gy~8$3-Z diff --git a/src/json2sql/__pycache__/cli.cpython-312.pyc b/src/json2sql/__pycache__/cli.cpython-312.pyc deleted file mode 100644 index 3499064fcb25b7bb4d8a919e5d48193138136ebe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3854 zcma)8U2GFq7QW*d+hfOeLTp1qAl%Rp;x;xE3avt^N|OL#Y4}N4i73*n@x4iG#xveK zV}dPH!L}8R)QaROsaC7fr&5sz9{X7B_F<)cNfub#5lBdMANpnrE3LXOd+vB9F@J^o zF!#(k=bn4cx%Yhc{39F=A{ZZk?#q1v)C2zGj)+4%8uTEvfCw5#L?9kj7#Cm`RZm_V z7X{9fR4MNr_u8~a^^N<8m-tkFULKc$=OunMkPnUr1>`~0ms{^{ur?E!1W0hH>AG0w zjfZF`*Rdvdsp}45+u7uD*Jk1&A+Qs6?bv#qYjT&O!5nt!wV6anGig~`cU`LUUbLFD zk|>Fh_$z37$a=DYw7r66JK0D&$fj4)+)O$_&&k!Kk8Gh{Dz<~u1k)htqU*@kRnBX) zm2}hA$q?E0iPz2Fc=WgPOL{&*tGcn5=x%S&jnnm{_iQghOor@*TpI!^c}ku{qP%_J z=rGP|CS{Z948@Z=!#P9Ob{T(EanZKY@{iU`fZ*<}}Rmv4WY^wY2)P z01{xd9vMA0GNzB7I69zf=O_b{!1WJi(<;rFmh74>?{VaqFTj&ol^Q!p+DseNG_a{- zuz-h0Mu$$0VZ%(DG*30tNR1^#OVZMLYDv>nEm+N(uB6$Np=1q(GsNJm`&R`;F(F`;|xy6&I?2Un$LuAkUKr8rcIM-U}LiU#=-gz)~I1pQqKn%(J`YS zu@4Q~J{qZVIGHpu(=?w>>Y7?A?>a;^${^`@;N;MOu^~Km;De(>4d;Nl8t&C}=ZF$1 zE11>5eTBPg`JH50UV8(}SC7r|I}Tb+_1zS1CgbCm${Ajmc>z7D*x*({ELuT1ez%?) zL|F1%clZ~4TFwOh`p7vj;r{bE=WqP0({2$MTVAp@@Zzb<++ect<6Lv(vtVO z&+#top<^cpf( zpQCQX_FM@3_G%KW1fJcqVa-c(O`0wQJ_^hTGw8f{8qElaa5?EBRC*0eUW`m-Z_%7g zzQZw*Y8f41W~%(=5Mw&)hw8O~1~(c4h%ICbv~C)z(nco0H^CAqV~iI9&=Odp08h>E z%UbKapj}i7hm-OKdk37UV=V`2j(h!hq}={-;ml{ zB!Y}5z3zDnr}%BQV_^K&S^@qP)V&EfreI$mAZt-IErA*wlJV;Jb#S16;*>#|F)^*@ z>BKRO4zlbyIx(nciuU6$aVTpZE}ofipO^`QW!8Q&YBp6Uv8~(*{^33ten60m=;4Tf zXHCDwam`*N)JW~_fUa5vm?<@D_7JK@bM;mxCY4 z27Z#mSAw&_<>J!901%JEaRVAwqAStyF<5H1}_fRB)_-o$ENkw zc;}sXY9*e!R{H+bay)f2{=3zT?h?YHFJck6r3DOZ1g_3&~udE>2r$?rIh%)`zK?NhP3b zq1c?x+E1q4*rYS2ZDl44{egQ$pfGgRfVZ7NbE1|~wg%%s_bt>RD{3RqZ-n6LWX>w4 zuH-WX)(1jMOcx654WJDE1Ywr2nOwXBs8d|PFaJgKp9q3;m;9fLpB{b$9P~scd8X`4 zz7NEcnKU^7*dy2YJ~})iRuIR#h73yyZ#g_Vtubo zQiAOSu_Y@?KAqJRC9%QsD+ z)b4w@|2?6j+S0Zdn~&5)pg}VduZ20)hC z|Akw4BqC39&BJ*gcve&J(Bl`vjtHn)tU)3QuR9W;U{fjdS^1x0JJidkLp2de8}0M? EKZ>T5N&o-= diff --git a/src/json2sql/__pycache__/cli.cpython-314.pyc b/src/json2sql/__pycache__/cli.cpython-314.pyc deleted file mode 100644 index 700dfef93ae04fe863be1d10ef6283b0c1a521d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4110 zcma)8-E$My6~C)ptyZ!m8%we+e_jDGR$>V-6jPHXCAN%3!HBivG&6$jBCYKOX;_eJ^L|G56Oo+f)4RG?LlZ2@yI}@5l>|)gMzmw>zSbq zdWfviS!Tv-crBXBvIcva;+d>(hBG)=qj+!DKNBzlL+BKn>vGjM-wDLCypQL2|2S>0 z8o^V+TwhbdrS>}n=;?RYns3lU@d41&=IQ~t!6x6O66jPQhuyE{n-B6KKFqg|Gxpl| z?dCi9ZG0ymc?n61kMc3T>m?++`5r#b_r8>5AKwphj<5Ey{6IJ22fI^-alQNh%m7=PIlHz~N)E0WZoWN#9)*4N;d29UCeJ4LCbHm*MBJZit3dmJ~xz z&nIbnC>7I5kH$gnn5=YB)(ySpPo;{s!8K2+SldZ#aTX?UMb(WZP0~kkc?G;OieZus zDM{Hn7;=c3FO@RHGbIW38@0qdAu%!Aa_F3EXQ9G=hM`NMA=%e2G+6EQrZs7{EI=>i1~Bo7&+1d%j z-RusO^!=Uu%?!v2_*x*oHGNKGO|Rt*leGh3`W$bXZMJI!a^|F0BL~xn7R^gk(Ym3T z%o$ZB+0Vj#DT{_q9I@S;nw-eLtxKAoUslUf{)8e;Y4Uj~KcyC`Ryya8$i__dOy0c| zd0i_sKMEx|U0E@Mf*>l8t`H7E(0V{~6drvRrd9MEdV}hFM5fJ-p5H}(6K>EzKMA66 ztPvtq4+^$7;)L3XLa|@Ze>~fGg?#Nn;m*chLcNYUyB_@rsK=lY6}CJLAB2YEGLQdg zLCFHzMMPi}!GwN@YtD_hy==`;%su1IdWAi%HRGGxifB_j=WEg~)dN=S&Rc* z|66vwL_GLF#s8*Xret)89zZgc4AxRE-iS?T_L39(NhzoR8cVgk8BJ5Q z38=*uY~WT!0FxEDBH5~;##%T7XhoT{q!9#UK#2r6(++{rI$?sffK_k=f(x~7OF1ss zph<9+0N4bct+f;MxuD6UvJ-=oA=9q`q6vm{(SY)^qMKgpcP1;UKWI?$ZAU~9>UnBz>@wjby2cbx^TW8Qn zDw}>%zHDp!jw<*s3lLc?873v^bUyu<##ti1M8uIjnL3vLPML;?i<-pvNvWwnY|m`dD;Ih7rGkw zD6kfp{`1sV+{w*wdX_NU5t3CT#SOIpkcI)ipo_yQnMjtY z=|JFc7V?x_2`oY`L0d91SrV&7=oUc9D4wipx~h$0$ta|g&@@WeSeC8c#p)}?f)ThN zLzCvB9wfC|(xJi9phGOJqywMBtSet7RuV` zWYUC9>iye?L80tBdbzIkUTdoxiL5>W- z3eN(xIAfj`r_$h5rWTr8Rxe<1l@9Gy8jjH>2+o%v)U11AHPW+5#zfjUDARV5=@l{^ zfT{IJK4+))Lty-jFiCp^$6}FpaFg+0I{N<6Yw??hZl>1SN4{o~))7rk5Xz#g2tqPu z`UIh<7NBXsu+j8YpffJXXRO}EWT84h1nq9dGGY{j>#8Jw(C3q2zru@PKW*bng9gb$_Jp|8YIq^TfyaJgamg z443Ru-!J+;79Tu+^gSR(tL z_`N-BBZxY?Z}ea9hihKvwppA#!09BM!5yFFKh2Xhs7plcVc-lBPOSIFvFpd`T*%cn z0h}1�NhMehM3a(@i$`m^k}StG}X=r@i5UPOI^jXUphE9v>BYY_ouBL|ZsC^{T@G Y3RE+}U)Ao>ameWpGmmJ*^jiM>FI$Cui~s-t diff --git a/src/json2sql/__pycache__/converter.cpython-312.pyc b/src/json2sql/__pycache__/converter.cpython-312.pyc deleted file mode 100644 index a5b1d3bb93c5186a54a2502550208ca248b427db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9957 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 diff --git a/src/json2sql/__pycache__/dialects.cpython-312.pyc b/src/json2sql/__pycache__/dialects.cpython-312.pyc deleted file mode 100644 index 52001fbc6e66ac0815764add88a7eca3cc32331b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4736 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 diff --git a/src/json2sql/__pycache__/dialects.cpython-314.pyc b/src/json2sql/__pycache__/dialects.cpython-314.pyc deleted file mode 100644 index 3cf880458b6171cb60149a185c2472f3310dba6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5821 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 a5B#orRs;V4p1I+(@tFh}=9f|-zWodAtZaAy diff --git a/tests/__pycache__/test_converter.cpython-312-pytest-9.0.3.pyc b/tests/__pycache__/test_converter.cpython-312-pytest-9.0.3.pyc deleted file mode 100644 index 1b1664f74fc8320d498413e4f64dae5e77388549..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24462 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 diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 0000000..88f5c29 --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,183 @@ +"""Tests for the json2sql CLI interface.""" + +import json +from json2sql.cli import app +from typer.testing import CliRunner + +runner = CliRunner() + + +class TestCLIBasic: + """Basic CLI command tests.""" + + def test_convert_json_file(self, tmp_path): + """Convert a simple JSON file to SQL via CLI.""" + data = {"name": "Alice", "age": 30} + json_file = tmp_path / "data.json" + json_file.write_text(json.dumps(data)) + + result = runner.invoke(app, ["convert", str(json_file)]) + assert result.exit_code == 0 + assert "CREATE TABLE" in result.stdout + assert "INSERT INTO" in result.stdout + assert "'Alice'" in result.stdout + assert "30" in result.stdout + + def test_convert_with_table_name(self, tmp_path): + """Specify custom table name.""" + json_file = tmp_path / "data.json" + json_file.write_text(json.dumps({"x": 1})) + + result = runner.invoke(app, ["convert", str(json_file), "--table", "my_table"]) + assert result.exit_code == 0 + assert "CREATE TABLE" in result.stdout + assert "my_table" in result.stdout + + def test_convert_with_dialect_mysql(self, tmp_path): + """Use MySQL dialect via CLI.""" + json_file = tmp_path / "data.json" + json_file.write_text(json.dumps({"active": True})) + + result = runner.invoke(app, ["convert", str(json_file), "--dialect", "mysql"]) + assert result.exit_code == 0 + assert "`active` TINYINT(1)" in result.stdout or "`active`" in result.stdout + + def test_convert_with_dialect_sqlite(self, tmp_path): + """Use SQLite dialect via CLI.""" + json_file = tmp_path / "data.json" + json_file.write_text(json.dumps({"price": 9.99})) + + result = runner.invoke(app, ["convert", str(json_file), "--dialect", "sqlite"]) + assert result.exit_code == 0 + assert "REAL" in result.stdout + + def test_convert_output_file(self, tmp_path): + """Write SQL to an output file.""" + json_file = tmp_path / "data.json" + json_file.write_text(json.dumps({"name": "test"})) + out_file = tmp_path / "out.sql" + + result = runner.invoke(app, ["convert", str(json_file), "--output", str(out_file)]) + assert result.exit_code == 0 + assert out_file.exists() + content = out_file.read_text() + assert "CREATE TABLE" in content + assert "INSERT INTO" in content + + def test_convert_with_flatten(self, tmp_path): + """Flatten nested JSON via CLI.""" + data = {"id": 1, "address": {"city": "NYC"}} + json_file = tmp_path / "nested.json" + json_file.write_text(json.dumps(data)) + + result = runner.invoke(app, ["convert", str(json_file), "--flatten"]) + assert result.exit_code == 0 + assert "CREATE TABLE" in result.stdout + + def test_convert_schema_only(self, tmp_path): + """Generate schema-only output (no INSERT).""" + json_file = tmp_path / "data.json" + json_file.write_text(json.dumps([{"name": "Alice", "age": 30}])) + + result = runner.invoke(app, ["convert", str(json_file), "--schema-only"]) + assert result.exit_code == 0 + assert "CREATE TABLE" in result.stdout + assert "INSERT INTO" not in result.stdout + + def test_convert_stdin(self): + """Read JSON from stdin.""" + result = runner.invoke(app, ["convert"], input=json.dumps({"name": "stdin_test"})) + assert result.exit_code == 0 + assert "'stdin_test'" in result.stdout + + def test_convert_empty_stdin_no_input(self): + """Error when no file and stdin is empty.""" + # Simulate no input (isatty = True in CliRunner) + result = runner.invoke(app, ["convert"]) + assert result.exit_code == 1 + assert "Error" in result.stderr or "Error" in result.stdout + + def test_convert_bad_json(self, tmp_path): + """Error on invalid JSON input.""" + json_file = tmp_path / "bad.json" + json_file.write_text("{invalid}") + + result = runner.invoke(app, ["convert", str(json_file)]) + assert result.exit_code == 1 + assert "Error" in result.stderr or "Error" in result.stdout + + def test_convert_bad_dialect(self, tmp_path): + """Error on invalid dialect.""" + json_file = tmp_path / "data.json" + json_file.write_text(json.dumps({"x": 1})) + + result = runner.invoke(app, ["convert", str(json_file), "--dialect", "oracle"]) + assert result.exit_code != 0 + + def test_convert_file_not_found(self): + """Error when file does not exist.""" + result = runner.invoke(app, ["convert", "nonexistent.json"]) + # Typer validates exists=True so it should fail + assert result.exit_code != 0 + + +class TestCLIVersion: + """Version command tests.""" + + def test_version(self): + """Show version.""" + result = runner.invoke(app, ["version"]) + assert result.exit_code == 0 + assert "0.1.0" in result.stdout + + +class TestCLIErrorHandling: + """Error handling tests.""" + + def test_no_args_shows_help(self): + """Running without args shows help.""" + result = runner.invoke(app) + # Typer with no_args_is_help may exit 0 or 2 depending on version + assert "Usage:" in result.stdout or "Usage:" in result.stderr or "Convert" in result.stdout or "Convert" in result.stderr + + def test_convert_array_of_objects(self, tmp_path): + """Convert array of objects via CLI.""" + data = [{"name": "Alice"}, {"name": "Bob"}] + json_file = tmp_path / "data.json" + json_file.write_text(json.dumps(data)) + + result = runner.invoke(app, ["convert", str(json_file)]) + assert result.exit_code == 0 + assert "'Alice'" in result.stdout + assert "'Bob'" in result.stdout + + def test_convert_empty_array(self, tmp_path): + """Empty array produces appropriate message.""" + json_file = tmp_path / "empty.json" + json_file.write_text("[]") + + result = runner.invoke(app, ["convert", str(json_file)]) + assert result.exit_code == 0 + assert "Empty" in result.stdout + + def test_convert_boolean_values(self, tmp_path): + """Boolean rendering depends on dialect.""" + data = {"flag": True, "active": False} + json_file = tmp_path / "data.json" + json_file.write_text(json.dumps(data)) + + # Postgres + result = runner.invoke(app, ["convert", str(json_file), "--dialect", "postgres"]) + assert result.exit_code == 0 + assert "TRUE" in result.stdout + assert "FALSE" in result.stdout + + def test_convert_null_values(self, tmp_path): + """NULL values handled.""" + data = {"name": None} + json_file = tmp_path / "data.json" + json_file.write_text(json.dumps(data)) + + result = runner.invoke(app, ["convert", str(json_file)]) + assert result.exit_code == 0 + assert "NULL" in result.stdout