Skip to content

Commit 5e89cdc

Browse files
authored
Merge pull request #3 from infactory-io/tech-1578-infactory-end-to-end-testing-for-cli
basic cli implemented
2 parents 98e9cc8 + 86d205a commit 5e89cdc

File tree

79 files changed

+2576
-22326
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+2576
-22326
lines changed

.flake8

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[flake8]
2+
max-line-length = 120
3+
max-complexity = 18
4+
select = B,C,E,F,W,T4,B9
5+
ignore = E203, E266, E501, W503, F403, E743, E741
6+
exclude =
7+
# list of ignores
8+
.git,
9+
__pycache__,
10+
.pytest_cache,
11+
.vscode

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.cql filter=lfs diff=lfs merge=lfs -text
2+
*.csv filter=lfs diff=lfs merge=lfs -text

.github/workflows/pypi.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,24 @@ jobs:
1616

1717
steps:
1818
- uses: actions/checkout@v4
19-
19+
2020
- name: Set up Python
2121
uses: actions/setup-python@v4
2222
with:
2323
python-version: '3.11'
24-
24+
2525
- name: Install Poetry
2626
run: |
2727
curl -sSL https://install.python-poetry.org | python3 -
2828
poetry --version
29-
29+
3030
- name: Install dependencies
3131
run: poetry install --only main
32-
32+
3333
- name: Build package
3434
run: poetry build
35-
35+
3636
- name: Publish to PyPI
3737
uses: pypa/gh-action-pypi-publish@release/v1
3838
with:
39-
packages-dir: dist/
39+
packages-dir: dist/

.github/workflows/python.yml

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,41 @@
33
#
44
# ref: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
55

6-
name: openapi_client Python package
6+
name: Python Tests
77

88
on: [push, pull_request]
99

1010
jobs:
11-
build:
12-
11+
test:
1312
runs-on: ubuntu-latest
1413
strategy:
1514
matrix:
16-
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
15+
python-version: ["3.10", "3.11", "3.12"]
1716

1817
steps:
1918
- uses: actions/checkout@v4
19+
2020
- name: Set up Python ${{ matrix.python-version }}
2121
uses: actions/setup-python@v4
2222
with:
2323
python-version: ${{ matrix.python-version }}
24+
25+
- name: Install Poetry
26+
run: |
27+
curl -sSL https://install.python-poetry.org | python3 -
28+
2429
- name: Install dependencies
2530
run: |
26-
python -m pip install --upgrade pip
27-
pip install -r requirements.txt
28-
pip install -r test-requirements.txt
29-
- name: Test with pytest
31+
poetry install
32+
33+
- name: Run tests with coverage
3034
run: |
31-
pytest --cov={{packageName}}
35+
poetry run coverage run -m pytest
36+
poetry run coverage report -m
37+
poetry run coverage html
38+
39+
- name: Upload coverage report
40+
uses: actions/upload-artifact@v3
41+
with:
42+
name: coverage-report
43+
path: htmlcov/

.isort.cfg

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[settings]
2+
line_length = 88
3+
multi_line_output = 3
4+
include_trailing_comma = True
5+
force_grid_wrap = 0
6+
use_parentheses = True
7+
ensure_newline_before_comments = True

.pre-commit-config.yaml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
minimum_pre_commit_version: '2.9.0'
2+
repos:
3+
- repo: https://github.com/pre-commit/pre-commit-hooks
4+
rev: v4.6.0
5+
hooks:
6+
- id: trailing-whitespace
7+
- id: end-of-file-fixer
8+
- id: mixed-line-ending
9+
args: ['--fix=lf']
10+
- id: check-added-large-files
11+
args: ['--maxkb=30000']
12+
# - id: no-commit-to-branch
13+
- repo: https://github.com/asottile/pyupgrade
14+
rev: v3.17.0
15+
hooks:
16+
- id: pyupgrade
17+
args: [--py311-plus]
18+
files: \.py$
19+
- repo: https://github.com/PyCQA/isort
20+
rev: 5.13.2
21+
hooks:
22+
- id: isort
23+
- repo: https://github.com/ambv/black
24+
rev: 24.8.0
25+
hooks:
26+
- id: black
27+
# - repo: https://github.com/myint/eradicate
28+
# rev: v2.1.0
29+
# hooks:
30+
# - id: eradicate
31+
- repo: https://github.com/PyCQA/flake8
32+
rev: 7.1.1
33+
hooks:
34+
- id: flake8
35+
# - repo: local
36+
# hooks:
37+
# - id: vulture
38+
# name: vulture
39+
# description: Find dead Python code
40+
# entry: vulture
41+
# args: ["--min-confidence", "80", "--exclude", "env,examples/human_eval,examples/leetcode_results", "."]
42+
# language: system
43+
# types: [python]
44+
- repo: https://github.com/PyCQA/autoflake
45+
rev: v2.3.1
46+
hooks:
47+
- id: autoflake
48+
args: [--in-place, --remove-all-unused-imports, --remove-unused-variables]
49+
types_or: [python, pyi]
50+
# - repo: https://github.com/adamchainz/blacken-docs
51+
# rev: 1.18.0
52+
# hooks:
53+
# - id: blacken-docs
54+
# additional_dependencies:
55+
# - black
56+
- repo: https://github.com/gitleaks/gitleaks
57+
rev: v8.20.0
58+
hooks:
59+
- id: gitleaks
60+
exclude: '^(frontends/.*|infactory/connectors/.*|infactory/storage/core_schema\.py|infactory/storage/schema\.py|infactory/storage/_static/.*|infactory/embeddings\.py)$'

README-tests.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ def test_projects_list(mocker):
2020
# Mock the HTTP response
2121
mock_response = [{"id": "proj-123", "name": "Test Project", "team_id": "team-456"}]
2222
mock_get = mocker.patch("infactory_client.client.Client._get", return_value=mock_response)
23-
23+
2424
# Create client and call the method
2525
client = Client(api_key="test_key")
2626
projects = client.projects.list(team_id="team-456")
27-
27+
2828
# Assertions
2929
mock_get.assert_called_once_with("v1/projects", {"team_id": "team-456"})
3030
assert len(projects) == 1
@@ -43,11 +43,11 @@ def test_projects_list_command(mocker):
4343
mock_client = MagicMock()
4444
mock_client.projects.list.return_value = mock_projects
4545
mocker.patch("infactory_cli.get_client", return_value=mock_client)
46-
46+
4747
# Call the CLI command handler
4848
args = MagicMock(team_id="team-456")
4949
handle_projects_list(args)
50-
50+
5151
# Assertions
5252
mock_client.projects.list.assert_called_once_with(team_id="team-456")
5353
```
@@ -69,12 +69,12 @@ def test_create_and_publish_query_program(requests_mock):
6969
# Mock API responses
7070
requests_mock.post("https://api.infactory.ai/v1/queryprograms", json={"id": "qp-123", "name": "Test Query"})
7171
requests_mock.patch("https://api.infactory.ai/v1/queryprograms/qp-123/publish", json={"id": "qp-123", "published": True})
72-
72+
7373
# Execute the workflow
7474
client = Client(api_key="test_key")
7575
query = client.query_programs.create(name="Test Query", dataline_id="dl-456", code="test code")
7676
published = client.query_programs.publish(query.id)
77-
77+
7878
# Assertions
7979
assert published.id == "qp-123"
8080
assert published.published is True
@@ -93,7 +93,7 @@ Record actual API responses and replay them in tests:
9393
def test_list_projects():
9494
client = Client(api_key="test_key")
9595
projects = client.projects.list(team_id="team-456")
96-
96+
9797
assert len(projects) > 0
9898
assert projects[0].id is not None
9999
```
@@ -112,19 +112,19 @@ E2E tests validate complete user workflows against the actual API:
112112
def test_e2e_datasource_workflow():
113113
# Use a test API key from environment variable
114114
client = Client(api_key=os.environ.get("NF_TEST_API_KEY"))
115-
115+
116116
# Create a project
117117
project = client.projects.create(name="Test Project", team_id=os.environ.get("NF_TEST_TEAM_ID"))
118-
118+
119119
# Create a datasource
120120
datasource = client.datasources.create(name="Test DB", project_id=project.id, type="postgres")
121-
121+
122122
# List datasources
123123
datasources = client.datasources.list(project_id=project.id)
124-
124+
125125
# Assertions
126126
assert any(ds.id == datasource.id for ds in datasources)
127-
127+
128128
# Clean up
129129
client.datasources.delete(datasource.id)
130130
client.projects.delete(project.id)
@@ -142,7 +142,7 @@ def test_cli_e2e():
142142
capture_output=True, text=True
143143
)
144144
assert "API key saved successfully" in result.stdout
145-
145+
146146
result = subprocess.run(
147147
["nf", "projects", "list", "--team-id", os.environ.get("NF_TEST_TEAM_ID")],
148148
capture_output=True, text=True

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ query_program = client.query_programs.create(
223223
question="What are the monthly sales by region?",
224224
code="""
225225
import pandas as pd
226-
226+
227227
def execute(df):
228228
# Group by month and region, sum sales
229229
result = df.groupby(['month', 'region'])['sales'].sum().reset_index()

examples.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,33 @@
55
from infactory_client.client import InfactoryClient
66

77

8-
def create_api_for_user():
8+
def create_api_for_user():
99
nf = InfactoryClient()
10-
nf.login() # This should open a browser window to login via Clerk and return with the JWT token
10+
nf.login() # This should open a browser window to login via Clerk and return with the JWT token
1111
# optionally
12-
nf.connect() # This should connect to the Infactory API using the API Key to generate the JWT token
12+
nf.connect() # This should connect to the Infactory API using the API Key to generate the JWT token
1313

1414
# The account creation should also run to create a default organization, team, user and project and set it to the state of the client
1515
state = nf.state
1616
print(state)
1717

1818
# The state serve as the default parameters for all requests
1919

20-
nf.upload("data/test.csv") # This should upload the file to the default project and create a dataline
21-
nf.explore(n=10) # This should generate 10 random questions and answers about the data - stored as query programs
22-
nf.query_programs.publish(all=True, public=True) # This should publish all query programs publicly
23-
print(nf.query_programs()) # This should list all query programs
24-
print(nf.project.docs.endpoint()) # This should return the project docs endpoint
25-
print(nf.project.docs.as_tools()) # Format the docs as tools to be used in a LLM chain
20+
nf.upload(
21+
"data/test.csv"
22+
) # This should upload the file to the default project and create a dataline
23+
nf.explore(
24+
n=10
25+
) # This should generate 10 random questions and answers about the data - stored as query programs
26+
nf.query_programs.publish(
27+
all=True, public=True
28+
) # This should publish all query programs publicly
29+
print(nf.query_programs()) # This should list all query programs
30+
print(nf.project.docs.endpoint()) # This should return the project docs endpoint
31+
print(
32+
nf.project.docs.as_tools()
33+
) # Format the docs as tools to be used in a LLM chain
34+
2635

2736
def ask_questions_of_a_dataset():
2837
nf = InfactoryClient()

0 commit comments

Comments
 (0)