swaggerforge 0.1.0__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. swaggerforge-0.3.0/PKG-INFO +170 -0
  2. swaggerforge-0.3.0/README.md +146 -0
  3. {swaggerforge-0.1.0 → swaggerforge-0.3.0}/pyproject.toml +3 -3
  4. swaggerforge-0.3.0/src/swaggerforge/__main__.py +5 -0
  5. swaggerforge-0.3.0/src/swaggerforge/auth.py +143 -0
  6. {swaggerforge-0.1.0 → swaggerforge-0.3.0}/src/swaggerforge/cli.py +43 -9
  7. swaggerforge-0.3.0/src/swaggerforge/config.py +206 -0
  8. {swaggerforge-0.1.0 → swaggerforge-0.3.0}/src/swaggerforge/models.py +1 -0
  9. swaggerforge-0.3.0/src/swaggerforge/output.py +91 -0
  10. {swaggerforge-0.1.0 → swaggerforge-0.3.0}/src/swaggerforge/parser.py +36 -3
  11. {swaggerforge-0.1.0 → swaggerforge-0.3.0}/src/swaggerforge/template.py +21 -5
  12. {swaggerforge-0.1.0 → swaggerforge-0.3.0}/src/swaggerforge/templates/test_file.py.j2 +6 -1
  13. swaggerforge-0.3.0/src/swaggerforge.egg-info/PKG-INFO +170 -0
  14. {swaggerforge-0.1.0 → swaggerforge-0.3.0}/src/swaggerforge.egg-info/SOURCES.txt +5 -0
  15. {swaggerforge-0.1.0 → swaggerforge-0.3.0}/src/swaggerforge.egg-info/requires.txt +5 -1
  16. swaggerforge-0.3.0/tests/test_auth.py +142 -0
  17. swaggerforge-0.3.0/tests/test_config.py +332 -0
  18. swaggerforge-0.3.0/tests/test_e2e_auth.py +109 -0
  19. {swaggerforge-0.1.0 → swaggerforge-0.3.0}/tests/test_generator.py +4 -4
  20. swaggerforge-0.3.0/tests/test_output.py +112 -0
  21. swaggerforge-0.3.0/tests/test_parser.py +200 -0
  22. swaggerforge-0.3.0/tests/test_template.py +138 -0
  23. swaggerforge-0.1.0/PKG-INFO +0 -99
  24. swaggerforge-0.1.0/README.md +0 -77
  25. swaggerforge-0.1.0/src/swaggerforge/__main__.py +0 -0
  26. swaggerforge-0.1.0/src/swaggerforge/output.py +0 -53
  27. swaggerforge-0.1.0/src/swaggerforge.egg-info/PKG-INFO +0 -99
  28. swaggerforge-0.1.0/tests/test_output.py +0 -46
  29. swaggerforge-0.1.0/tests/test_parser.py +0 -79
  30. swaggerforge-0.1.0/tests/test_template.py +0 -73
  31. {swaggerforge-0.1.0 → swaggerforge-0.3.0}/LICENSE +0 -0
  32. {swaggerforge-0.1.0 → swaggerforge-0.3.0}/setup.cfg +0 -0
  33. {swaggerforge-0.1.0 → swaggerforge-0.3.0}/src/swaggerforge/__init__.py +0 -0
  34. {swaggerforge-0.1.0 → swaggerforge-0.3.0}/src/swaggerforge/generator.py +0 -0
  35. {swaggerforge-0.1.0 → swaggerforge-0.3.0}/src/swaggerforge/validator.py +0 -0
  36. {swaggerforge-0.1.0 → swaggerforge-0.3.0}/src/swaggerforge.egg-info/dependency_links.txt +0 -0
  37. {swaggerforge-0.1.0 → swaggerforge-0.3.0}/src/swaggerforge.egg-info/entry_points.txt +0 -0
  38. {swaggerforge-0.1.0 → swaggerforge-0.3.0}/src/swaggerforge.egg-info/top_level.txt +0 -0
  39. {swaggerforge-0.1.0 → swaggerforge-0.3.0}/tests/test_validator.py +0 -0
@@ -0,0 +1,170 @@
1
+ Metadata-Version: 2.4
2
+ Name: swaggerforge
3
+ Version: 0.3.0
4
+ Summary: Automatic pytest test generation from OpenAPI (Swagger) specifications
5
+ Author: Viktor Pylypenko
6
+ License-Expression: MIT
7
+ Keywords: openapi,swagger,pytest,test generation,api testing
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: click>=8.0
12
+ Requires-Dist: prance>=23.6.21.0
13
+ Requires-Dist: openapi-spec-validator>=0.7
14
+ Requires-Dist: jinja2>=3.0
15
+ Requires-Dist: requests>=2.28
16
+ Requires-Dist: jsonschema>=4.0
17
+ Requires-Dist: tomli>=2.0; python_version < "3.11"
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest>=9.0; extra == "dev"
20
+ Requires-Dist: flake8>=7.0; extra == "dev"
21
+ Requires-Dist: pytest-cov>=7.0; extra == "dev"
22
+ Requires-Dist: tox>=4.0; extra == "dev"
23
+ Dynamic: license-file
24
+
25
+ # SwaggerForge
26
+
27
+ Automatic pytest test generation from OpenAPI (Swagger) specifications.
28
+
29
+ SwaggerForge is a Python library and command-line tool that reads an OpenAPI
30
+ specification and generates ready-to-run pytest test files - one per resource
31
+ covering positive, negative, boundary, and boolean scenarios grounded in
32
+ established test-design techniques.
33
+
34
+ ## Features
35
+
36
+ - Reads OpenAPI 3.x specifications in JSON or YAML
37
+ - Resolves `$ref` references automatically
38
+ - Generates one pytest file per resource tag
39
+ - Produces six scenario types per endpoint where applicable:
40
+ - **Positive** >>> valid request, expects a 2xx response and validates the
41
+ response schema
42
+ - **Missing required field** >>> omits a required field, expects 400
43
+ - **Wrong data type** >>> sends a mistyped field, expects 400/422
44
+ - **Nonexistent resource** >>> requests an unlikely identifier, expects 404
45
+ - **Boundary values** >>> tests values at and just beyond declared
46
+ numeric/length limits (Boundary Value Analysis)
47
+ - **Boolean coverage** >>> exercises both `true` and `false` for boolean fields
48
+ - Deterministic output: the same specification always produces identical tests
49
+ - Generated files use session-scoped pytest fixtures and run with no manual edits
50
+ - Optional `swaggerforge.toml` config file for project-level defaults
51
+ - Authentication support: bearer, API key, and basic schemes, with
52
+ environment-variable substitution for secrets
53
+
54
+ ## Requirements
55
+
56
+ - Python 3.10 or newer
57
+
58
+ ## Installation
59
+
60
+ ```bash
61
+ pip install swaggerforge
62
+ ```
63
+
64
+ ## Usage
65
+
66
+ Generate tests from a specification, pointing at the base URL of the API
67
+ under test:
68
+
69
+ ```bash
70
+ swaggerforge generate --spec swagger.json --url http://localhost:8080
71
+ ```
72
+
73
+ This reads `swagger.json`, writes one `test_<resource>.py` file per resource
74
+ tag into the output directory (default: `tests_generated/`), and the files can
75
+ be run immediately:
76
+
77
+ ```bash
78
+ pytest tests_generated
79
+ ```
80
+
81
+ ### Options
82
+
83
+ | Option | Description | Default |
84
+ |------------|----------------------------------------------------|-----------------------------------|
85
+ | `--spec` | Path to the OpenAPI specification (JSON or YAML) | *(required)* |
86
+ | `--url` | Base URL of the API under test | *(required unless in config)* |
87
+ | `--output` | Directory for the generated test files | `tests_generated` |
88
+ | `--config` | Path to a configuration file | `./swaggerforge.toml` if present |
89
+
90
+ ## Configuration
91
+
92
+ Options that stay the same across runs can be kept in a `swaggerforge.toml`
93
+ file instead of being passed on the command line. The file is picked up
94
+ automatically from the directory where the tool is run, or an explicit path
95
+ can be given with `--config`.
96
+
97
+ ```toml
98
+ # swaggerforge.toml
99
+ base_url = "http://localhost:8080"
100
+ output_dir = "tests_generated"
101
+ timeout = 30
102
+ ```
103
+
104
+ | Key | Type | Effect |
105
+ |--------------|---------|---------------------------------------------------------------|
106
+ | `base_url` | string | Base URL of the API; makes `--url` optional |
107
+ | `output_dir` | string | Directory for generated files |
108
+ | `timeout` | integer | Embeds `timeout=<n>` into every generated HTTP request call |
109
+
110
+ Values given on the command line always take precedence over the config file.
111
+ Without a `timeout`, generated tests place no time limit on requests -
112
+ setting one makes test runs fail fast when the API is unreachable.
113
+
114
+ ## Authentication
115
+
116
+ When a specification declares security requirements, SwaggerForge embeds the
117
+ matching authentication headers into the generated tests. Credentials are
118
+ supplied through the config file, never taken from the specification itself.
119
+
120
+ ```toml
121
+ # swaggerforge.toml
122
+ [auth.bearer]
123
+ token = "${API_TOKEN}"
124
+
125
+ [auth.api_key]
126
+ value = "${API_KEY}"
127
+
128
+ [auth.basic]
129
+ username = "${API_USER}"
130
+ password = "${API_PASSWORD}"
131
+ ```
132
+
133
+ Three scheme kinds are supported, matching the OpenAPI security scheme types:
134
+
135
+ | Section | Applies to | Generated header |
136
+ |-----------------|---------------------------------------------|-------------------------------------------|
137
+ | `[auth.bearer]` | `http` bearer, and `oauth2` (pre-obtained token) | `Authorization: Bearer <token>` |
138
+ | `[auth.api_key]`| `apiKey` in header | the header named by the specification |
139
+ | `[auth.basic]` | `http` basic | `Authorization: Basic <base64>` |
140
+
141
+ For an API key, the header **name** comes from the specification's scheme
142
+ definition; the config supplies only the secret `value`.
143
+
144
+ **Environment variables.** Any auth value may reference an environment
145
+ variable with `${VAR}` syntax, so secrets stay out of the config file. A
146
+ referenced variable that is not set is an error.
147
+
148
+ **Unconfigured or unsupported schemes.** If a specification requires a scheme
149
+ whose credentials are not configured, SwaggerForge prints a warning and
150
+ generates the tests without that header, rather than failing. Schemes that are
151
+ not yet supported - API keys in a query string or cookie, and OpenID Connect -
152
+ are likewise skipped with a warning.
153
+
154
+ ## How it works
155
+
156
+ SwaggerForge runs a six-stage pipeline: the specification is validated, parsed
157
+ into an internal model (with `$ref`s resolved), turned into test scenarios
158
+ based on test-design techniques, rendered into pytest code via templates, and
159
+ written to per-resource files.
160
+
161
+ ## Limitations
162
+
163
+ - Targets OpenAPI 3.x with JSON request bodies
164
+ - Authentication covers bearer, API key (header), and basic schemes; API keys
165
+ in query or cookie, full OAuth2 flows, and OpenID Connect are not yet handled
166
+ - Boundary tests require the specification to declare numeric/length constraints
167
+
168
+ ## License
169
+
170
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file.
@@ -0,0 +1,146 @@
1
+ # SwaggerForge
2
+
3
+ Automatic pytest test generation from OpenAPI (Swagger) specifications.
4
+
5
+ SwaggerForge is a Python library and command-line tool that reads an OpenAPI
6
+ specification and generates ready-to-run pytest test files - one per resource
7
+ covering positive, negative, boundary, and boolean scenarios grounded in
8
+ established test-design techniques.
9
+
10
+ ## Features
11
+
12
+ - Reads OpenAPI 3.x specifications in JSON or YAML
13
+ - Resolves `$ref` references automatically
14
+ - Generates one pytest file per resource tag
15
+ - Produces six scenario types per endpoint where applicable:
16
+ - **Positive** >>> valid request, expects a 2xx response and validates the
17
+ response schema
18
+ - **Missing required field** >>> omits a required field, expects 400
19
+ - **Wrong data type** >>> sends a mistyped field, expects 400/422
20
+ - **Nonexistent resource** >>> requests an unlikely identifier, expects 404
21
+ - **Boundary values** >>> tests values at and just beyond declared
22
+ numeric/length limits (Boundary Value Analysis)
23
+ - **Boolean coverage** >>> exercises both `true` and `false` for boolean fields
24
+ - Deterministic output: the same specification always produces identical tests
25
+ - Generated files use session-scoped pytest fixtures and run with no manual edits
26
+ - Optional `swaggerforge.toml` config file for project-level defaults
27
+ - Authentication support: bearer, API key, and basic schemes, with
28
+ environment-variable substitution for secrets
29
+
30
+ ## Requirements
31
+
32
+ - Python 3.10 or newer
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pip install swaggerforge
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ Generate tests from a specification, pointing at the base URL of the API
43
+ under test:
44
+
45
+ ```bash
46
+ swaggerforge generate --spec swagger.json --url http://localhost:8080
47
+ ```
48
+
49
+ This reads `swagger.json`, writes one `test_<resource>.py` file per resource
50
+ tag into the output directory (default: `tests_generated/`), and the files can
51
+ be run immediately:
52
+
53
+ ```bash
54
+ pytest tests_generated
55
+ ```
56
+
57
+ ### Options
58
+
59
+ | Option | Description | Default |
60
+ |------------|----------------------------------------------------|-----------------------------------|
61
+ | `--spec` | Path to the OpenAPI specification (JSON or YAML) | *(required)* |
62
+ | `--url` | Base URL of the API under test | *(required unless in config)* |
63
+ | `--output` | Directory for the generated test files | `tests_generated` |
64
+ | `--config` | Path to a configuration file | `./swaggerforge.toml` if present |
65
+
66
+ ## Configuration
67
+
68
+ Options that stay the same across runs can be kept in a `swaggerforge.toml`
69
+ file instead of being passed on the command line. The file is picked up
70
+ automatically from the directory where the tool is run, or an explicit path
71
+ can be given with `--config`.
72
+
73
+ ```toml
74
+ # swaggerforge.toml
75
+ base_url = "http://localhost:8080"
76
+ output_dir = "tests_generated"
77
+ timeout = 30
78
+ ```
79
+
80
+ | Key | Type | Effect |
81
+ |--------------|---------|---------------------------------------------------------------|
82
+ | `base_url` | string | Base URL of the API; makes `--url` optional |
83
+ | `output_dir` | string | Directory for generated files |
84
+ | `timeout` | integer | Embeds `timeout=<n>` into every generated HTTP request call |
85
+
86
+ Values given on the command line always take precedence over the config file.
87
+ Without a `timeout`, generated tests place no time limit on requests -
88
+ setting one makes test runs fail fast when the API is unreachable.
89
+
90
+ ## Authentication
91
+
92
+ When a specification declares security requirements, SwaggerForge embeds the
93
+ matching authentication headers into the generated tests. Credentials are
94
+ supplied through the config file, never taken from the specification itself.
95
+
96
+ ```toml
97
+ # swaggerforge.toml
98
+ [auth.bearer]
99
+ token = "${API_TOKEN}"
100
+
101
+ [auth.api_key]
102
+ value = "${API_KEY}"
103
+
104
+ [auth.basic]
105
+ username = "${API_USER}"
106
+ password = "${API_PASSWORD}"
107
+ ```
108
+
109
+ Three scheme kinds are supported, matching the OpenAPI security scheme types:
110
+
111
+ | Section | Applies to | Generated header |
112
+ |-----------------|---------------------------------------------|-------------------------------------------|
113
+ | `[auth.bearer]` | `http` bearer, and `oauth2` (pre-obtained token) | `Authorization: Bearer <token>` |
114
+ | `[auth.api_key]`| `apiKey` in header | the header named by the specification |
115
+ | `[auth.basic]` | `http` basic | `Authorization: Basic <base64>` |
116
+
117
+ For an API key, the header **name** comes from the specification's scheme
118
+ definition; the config supplies only the secret `value`.
119
+
120
+ **Environment variables.** Any auth value may reference an environment
121
+ variable with `${VAR}` syntax, so secrets stay out of the config file. A
122
+ referenced variable that is not set is an error.
123
+
124
+ **Unconfigured or unsupported schemes.** If a specification requires a scheme
125
+ whose credentials are not configured, SwaggerForge prints a warning and
126
+ generates the tests without that header, rather than failing. Schemes that are
127
+ not yet supported - API keys in a query string or cookie, and OpenID Connect -
128
+ are likewise skipped with a warning.
129
+
130
+ ## How it works
131
+
132
+ SwaggerForge runs a six-stage pipeline: the specification is validated, parsed
133
+ into an internal model (with `$ref`s resolved), turned into test scenarios
134
+ based on test-design techniques, rendered into pytest code via templates, and
135
+ written to per-resource files.
136
+
137
+ ## Limitations
138
+
139
+ - Targets OpenAPI 3.x with JSON request bodies
140
+ - Authentication covers bearer, API key (header), and basic schemes; API keys
141
+ in query or cookie, full OAuth2 flows, and OpenID Connect are not yet handled
142
+ - Boundary tests require the specification to declare numeric/length constraints
143
+
144
+ ## License
145
+
146
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "swaggerforge"
7
- version = "0.1.0"
7
+ version = "0.3.0"
8
8
  description = "Automatic pytest test generation from OpenAPI (Swagger) specifications"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -16,11 +16,11 @@ keywords = ["openapi", "swagger", "pytest", "test generation", "api testing"]
16
16
  dependencies = [
17
17
  "click>=8.0",
18
18
  "prance>=23.6.21.0",
19
- "openapi-core>=0.18",
20
19
  "openapi-spec-validator>=0.7",
21
20
  "jinja2>=3.0",
22
21
  "requests>=2.28",
23
22
  "jsonschema>=4.0",
23
+ 'tomli>=2.0; python_version < "3.11"',
24
24
  ]
25
25
 
26
26
  [project.scripts]
@@ -38,4 +38,4 @@ addopts = "--import-mode=importlib"
38
38
  testpaths = ["tests"]
39
39
 
40
40
  [project.optional-dependencies]
41
- dev = ["pytest>=9.0", "flake8>=7.0"]
41
+ dev = ["pytest>=9.0", "flake8>=7.0", "pytest-cov>=7.0", "tox>=4.0"]
@@ -0,0 +1,5 @@
1
+ """Entry point for running SwaggerForge as a module (python -m swaggerforge)."""
2
+ from swaggerforge.cli import main
3
+
4
+ if __name__ == "__main__":
5
+ main()
@@ -0,0 +1,143 @@
1
+ """Resolution of authentication for generated tests.
2
+
3
+ Given an endpoint's declared security requirement, the specification's
4
+ security schemes, and the user's configured credentials, this module
5
+ decides which authentication headers to inject into the generated tests.
6
+
7
+ Supported schemes (v0.3.0): HTTP bearer, HTTP basic, and API key in a
8
+ header. OAuth2 is treated as bearer (the user supplies a pre-obtained
9
+ token). Other schemes - API key in query or cookie, OpenID Connect,
10
+ and any undefined scheme name - are skipped with a warning.
11
+ """
12
+
13
+ import base64
14
+ from typing import NamedTuple
15
+
16
+
17
+ class ResolvedAuth(NamedTuple):
18
+ """The outcome of resolving auth for one endpoint.
19
+
20
+ ``headers`` holds the authentication headers to inject (empty when no
21
+ auth applies). ``warning`` is a human-readable message when a declared
22
+ scheme was skipped, or None otherwise.
23
+ """
24
+
25
+ headers: dict
26
+ warning: str | None
27
+
28
+
29
+ _NO_AUTH = ResolvedAuth(headers={}, warning=None)
30
+
31
+
32
+ def resolve_auth(endpoint, security_schemes, auth_config):
33
+ """Resolve the authentication headers for a single endpoint.
34
+
35
+ Args:
36
+ endpoint: The Endpoint whose security requirement is resolved.
37
+ security_schemes: The specification's securitySchemes definitions.
38
+ auth_config: The AuthConfig carrying user-supplied credentials.
39
+
40
+ Returns:
41
+ A ResolvedAuth with the headers to inject and an optional warning.
42
+
43
+ Raises:
44
+ AuthError: If a supported scheme is required but its credentials
45
+ are not configured.
46
+ """
47
+ if not endpoint.security:
48
+ return _NO_AUTH
49
+
50
+ scheme_name = endpoint.security[0]
51
+ scheme = security_schemes.get(scheme_name)
52
+ if scheme is None:
53
+ return ResolvedAuth(
54
+ headers={},
55
+ warning=(
56
+ f"Security scheme '{scheme_name}' required by "
57
+ f"{endpoint.method.upper()} {endpoint.path} is not defined in "
58
+ f"the specification; skipping authentication for it."
59
+ ),
60
+ )
61
+
62
+ scheme_type = scheme.get("type")
63
+
64
+ if scheme_type == "oauth2":
65
+ return _resolve_bearer(scheme_name, auth_config)
66
+ if scheme_type == "http":
67
+ http_scheme = scheme.get("scheme", "").lower()
68
+ if http_scheme == "bearer":
69
+ return _resolve_bearer(scheme_name, auth_config)
70
+ if http_scheme == "basic":
71
+ return _resolve_basic(scheme_name, auth_config)
72
+ if scheme_type == "apiKey" and scheme.get("in") == "header":
73
+ return _resolve_api_key(scheme_name, scheme, auth_config)
74
+
75
+ return ResolvedAuth(
76
+ headers={},
77
+ warning=(
78
+ f"Security scheme '{scheme_name}' (type '{scheme_type}') is not "
79
+ f"supported in this version; skipping authentication for "
80
+ f"{endpoint.method.upper()} {endpoint.path}."
81
+ ),
82
+ )
83
+
84
+
85
+ def _resolve_bearer(scheme_name, auth_config):
86
+ """Build a bearer Authorization header from configured credentials."""
87
+ bearer = auth_config.bearer
88
+ if bearer is None or not bearer.token:
89
+ return ResolvedAuth(
90
+ headers={},
91
+ warning=(
92
+ f"Scheme '{scheme_name}' requires a bearer token, but none is "
93
+ f"configured; generating without authentication. Add a "
94
+ f"[auth.bearer] section with a 'token' to enable it."
95
+ ),
96
+ )
97
+ return ResolvedAuth(
98
+ headers={"Authorization": f"Bearer {bearer.token}"}, warning=None
99
+ )
100
+
101
+
102
+ def _resolve_basic(scheme_name, auth_config):
103
+ """Build a basic Authorization header from configured credentials."""
104
+ basic = auth_config.basic
105
+ if basic is None or not basic.username or not basic.password:
106
+ return ResolvedAuth(
107
+ headers={},
108
+ warning=(
109
+ f"Scheme '{scheme_name}' requires basic credentials, but the "
110
+ f"username and password are not both configured; generating "
111
+ f"without authentication. Add a [auth.basic] section with "
112
+ f"'username' and 'password' to enable it."
113
+ ),
114
+ )
115
+ raw = f"{basic.username}:{basic.password}".encode("utf-8")
116
+ encoded = base64.b64encode(raw).decode("ascii")
117
+ return ResolvedAuth(
118
+ headers={"Authorization": f"Basic {encoded}"}, warning=None
119
+ )
120
+
121
+
122
+ def _resolve_api_key(scheme_name, scheme, auth_config):
123
+ """Build an API-key header, taking the header name from the spec scheme."""
124
+ api_key = auth_config.api_key
125
+ if api_key is None or not api_key.value:
126
+ return ResolvedAuth(
127
+ headers={},
128
+ warning=(
129
+ f"Scheme '{scheme_name}' requires an API key, but none is "
130
+ f"configured; generating without authentication. Add a "
131
+ f"[auth.api_key] section with a 'value' to enable it."
132
+ ),
133
+ )
134
+ header_name = scheme.get("name")
135
+ if not header_name:
136
+ return ResolvedAuth(
137
+ headers={},
138
+ warning=(
139
+ f"API-key scheme '{scheme_name}' does not declare a header "
140
+ f"name; skipping authentication for it."
141
+ ),
142
+ )
143
+ return ResolvedAuth(headers={header_name: api_key.value}, warning=None)
@@ -1,6 +1,7 @@
1
1
  """Command-line interface for SwaggerForge."""
2
2
 
3
3
  import click
4
+ from swaggerforge.config import load_config, ConfigError
4
5
  from swaggerforge.validator import load_and_validate, SpecValidationError
5
6
  from swaggerforge.parser import parse_spec, SpecParseError
6
7
  from swaggerforge.generator import generate_scenarios
@@ -23,17 +24,39 @@ def main():
23
24
  )
24
25
  @click.option(
25
26
  "--url",
26
- required=True,
27
- help="Base URL of the API to be tested.",
27
+ default=None,
28
+ help="Base URL of the API to be tested. "
29
+ "Required unless base_url is set in the config file.",
28
30
  )
29
31
  @click.option(
30
32
  "--output",
31
- default="tests_generated",
33
+ default=None,
34
+ type=click.Path(),
35
+ help="Directory where generated test files will be written "
36
+ "[default: tests_generated].",
37
+ )
38
+ @click.option(
39
+ "--config",
40
+ "config_path",
41
+ default=None,
32
42
  type=click.Path(),
33
- help="Directory where generated test files will be written.",
43
+ help="Path to a swaggerforge.toml config file "
44
+ "[default: ./swaggerforge.toml if present].",
34
45
  )
35
- def generate(spec, url, output):
46
+ def generate(spec, url, output, config_path):
36
47
  """Generate pytest test files from an OpenAPI specification."""
48
+ try:
49
+ config = load_config(config_path)
50
+ except ConfigError as error:
51
+ raise click.ClickException(str(error))
52
+
53
+ url = url or config.base_url
54
+ if url is None:
55
+ raise click.ClickException(
56
+ "No base URL given: pass --url or set base_url in swaggerforge.toml."
57
+ )
58
+ output = output or config.output_dir or "tests_generated"
59
+
37
60
  click.echo(f"Reading specification: {spec}")
38
61
 
39
62
  try:
@@ -45,7 +68,8 @@ def generate(spec, url, output):
45
68
  click.echo(f"API title: {spec_dict['info']['title']}")
46
69
 
47
70
  try:
48
- endpoints = parse_spec(spec)
71
+ parsed = parse_spec(spec)
72
+ endpoints = parsed.endpoints
49
73
  except SpecParseError as error:
50
74
  raise click.ClickException(str(error))
51
75
 
@@ -56,12 +80,22 @@ def generate(spec, url, output):
56
80
  click.echo(f"Generated {len(scenarios)} test scenarios.")
57
81
 
58
82
  try:
59
- written = write_test_files(scenarios, url, output)
83
+ result = write_test_files(
84
+ scenarios,
85
+ url,
86
+ output,
87
+ config.timeout,
88
+ security_schemes=parsed.security_schemes,
89
+ auth_config=config.auth,
90
+ )
60
91
  except OSError as error:
61
92
  raise click.ClickException(
62
93
  f"Could not write test files to '{output}': {error}"
63
94
  )
64
95
 
65
- click.echo(f"Wrote {len(written)} test file(s) to '{output}':")
66
- for path in written:
96
+ for warning in result.warnings:
97
+ click.echo(f"Warning: {warning}")
98
+
99
+ click.echo(f"Wrote {len(result.written)} test file(s) to '{output}':")
100
+ for path in result.written:
67
101
  click.echo(f" - {path}")