mreg-api 0.2.3__tar.gz → 0.4.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 (97) hide show
  1. mreg_api-0.4.0/.github/workflows/docs.yml +29 -0
  2. mreg_api-0.4.0/.github/workflows/test.yml +52 -0
  3. {mreg_api-0.2.3 → mreg_api-0.4.0}/.gitignore +5 -0
  4. {mreg_api-0.2.3 → mreg_api-0.4.0}/.markdownlint.json +5 -1
  5. {mreg_api-0.2.3 → mreg_api-0.4.0}/CHANGELOG.md +48 -0
  6. {mreg_api-0.2.3 → mreg_api-0.4.0}/PKG-INFO +24 -3
  7. mreg_api-0.4.0/README.md +44 -0
  8. mreg_api-0.4.0/TESTING.md +152 -0
  9. mreg_api-0.4.0/ci/Dockerfile +15 -0
  10. mreg_api-0.4.0/ci/docker-compose.yml +40 -0
  11. mreg_api-0.4.0/ci/run_tests.sh +121 -0
  12. mreg_api-0.4.0/docs/guides/authentication.md +62 -0
  13. mreg_api-0.4.0/docs/guides/caching.md +63 -0
  14. mreg_api-0.4.0/docs/guides/configuration.md +64 -0
  15. mreg_api-0.4.0/docs/guides/eventlog.md +52 -0
  16. mreg_api-0.4.0/docs/guides/requesthistory.md +15 -0
  17. mreg_api-0.4.0/docs/guides/resources.md +163 -0
  18. mreg_api-0.4.0/docs/index.md +50 -0
  19. mreg_api-0.4.0/docs/quick-start.md +122 -0
  20. mreg_api-0.4.0/docs/reference/cache.md +15 -0
  21. mreg_api-0.4.0/docs/reference/client.md +14 -0
  22. mreg_api-0.4.0/docs/reference/events.md +25 -0
  23. mreg_api-0.4.0/docs/reference/exceptions.md +12 -0
  24. mreg_api-0.4.0/docs/reference/managers.md +123 -0
  25. mreg_api-0.4.0/docs/reference/models.md +11 -0
  26. {mreg_api-0.2.3 → mreg_api-0.4.0}/mreg_api/_version.py +3 -3
  27. {mreg_api-0.2.3 → mreg_api-0.4.0}/mreg_api/cache.py +27 -2
  28. {mreg_api-0.2.3 → mreg_api-0.4.0}/mreg_api/client.py +261 -185
  29. {mreg_api-0.2.3 → mreg_api-0.4.0}/mreg_api/endpoints.py +3 -0
  30. {mreg_api-0.2.3 → mreg_api-0.4.0}/mreg_api/events.py +87 -10
  31. mreg_api-0.4.0/mreg_api/managers.py +4793 -0
  32. {mreg_api-0.2.3 → mreg_api-0.4.0}/mreg_api/models/__init__.py +2 -0
  33. mreg_api-0.4.0/mreg_api/models/abstracts.py +33 -0
  34. mreg_api-0.4.0/mreg_api/models/fields.py +198 -0
  35. {mreg_api-0.2.3 → mreg_api-0.4.0}/mreg_api/models/history.py +0 -30
  36. mreg_api-0.4.0/mreg_api/models/models.py +1297 -0
  37. {mreg_api-0.2.3 → mreg_api-0.4.0}/mreg_api/types.py +1 -11
  38. {mreg_api-0.2.3 → mreg_api-0.4.0}/mreg_api.egg-info/PKG-INFO +24 -3
  39. mreg_api-0.4.0/mreg_api.egg-info/SOURCES.txt +86 -0
  40. {mreg_api-0.2.3 → mreg_api-0.4.0}/mreg_api.egg-info/requires.txt +1 -1
  41. mreg_api-0.4.0/mreg_api.egg-info/scm_file_list.json +82 -0
  42. mreg_api-0.4.0/mreg_api.egg-info/scm_version.json +8 -0
  43. {mreg_api-0.2.3 → mreg_api-0.4.0}/pyproject.toml +61 -23
  44. mreg_api-0.4.0/tests/__init__.py +0 -0
  45. mreg_api-0.4.0/tests/conftest.py +12 -0
  46. mreg_api-0.4.0/tests/integration/__init__.py +0 -0
  47. mreg_api-0.4.0/tests/integration/conftest.py +226 -0
  48. mreg_api-0.4.0/tests/integration/test_atoms.py +302 -0
  49. mreg_api-0.4.0/tests/integration/test_cname.py +175 -0
  50. mreg_api-0.4.0/tests/integration/test_dhcp.py +68 -0
  51. mreg_api-0.4.0/tests/integration/test_host_records.py +773 -0
  52. mreg_api-0.4.0/tests/integration/test_hostgroups.py +284 -0
  53. mreg_api-0.4.0/tests/integration/test_hosts.py +227 -0
  54. mreg_api-0.4.0/tests/integration/test_ip.py +154 -0
  55. mreg_api-0.4.0/tests/integration/test_labels.py +185 -0
  56. mreg_api-0.4.0/tests/integration/test_meta.py +53 -0
  57. mreg_api-0.4.0/tests/integration/test_nameservers.py +130 -0
  58. mreg_api-0.4.0/tests/integration/test_network_policy.py +362 -0
  59. mreg_api-0.4.0/tests/integration/test_networks.py +298 -0
  60. mreg_api-0.4.0/tests/integration/test_permissions.py +202 -0
  61. mreg_api-0.4.0/tests/integration/test_roles.py +323 -0
  62. mreg_api-0.4.0/tests/integration/test_zones.py +242 -0
  63. mreg_api-0.4.0/tests/models/test_fields.py +457 -0
  64. {mreg_api-0.2.3 → mreg_api-0.4.0}/tests/models/test_models.py +20 -2
  65. {mreg_api-0.2.3 → mreg_api-0.4.0}/tests/test_client.py +142 -196
  66. {mreg_api-0.2.3 → mreg_api-0.4.0}/tests/test_events.py +20 -24
  67. {mreg_api-0.2.3 → mreg_api-0.4.0}/tests/test_exceptions.py +1 -5
  68. mreg_api-0.4.0/tests/test_managers.py +173 -0
  69. {mreg_api-0.2.3 → mreg_api-0.4.0}/uv.lock +368 -11
  70. mreg_api-0.4.0/zensical.toml +398 -0
  71. mreg_api-0.2.3/.github/workflows/test.yml +0 -30
  72. mreg_api-0.2.3/README.md +0 -23
  73. mreg_api-0.2.3/mreg_api/models/abstracts.py +0 -558
  74. mreg_api-0.2.3/mreg_api/models/fields.py +0 -198
  75. mreg_api-0.2.3/mreg_api/models/models.py +0 -4435
  76. mreg_api-0.2.3/mreg_api.egg-info/SOURCES.txt +0 -45
  77. mreg_api-0.2.3/tests/conftest.py +0 -39
  78. mreg_api-0.2.3/tests/models/test_fields.py +0 -275
  79. mreg_api-0.2.3/tests/models/test_history.py +0 -40
  80. /mreg_api-0.2.3/mreg_api/py.typed → /mreg_api-0.4.0/.claude/CLAUDE.md +0 -0
  81. {mreg_api-0.2.3 → mreg_api-0.4.0}/.github/workflows/publish.yml +0 -0
  82. {mreg_api-0.2.3 → mreg_api-0.4.0}/.pre-commit-config.yaml +0 -0
  83. {mreg_api-0.2.3 → mreg_api-0.4.0}/LICENSE +0 -0
  84. {mreg_api-0.2.3 → mreg_api-0.4.0}/NOTES.md +0 -0
  85. {mreg_api-0.2.3 → mreg_api-0.4.0}/mreg_api/__about__.py +0 -0
  86. {mreg_api-0.2.3 → mreg_api-0.4.0}/mreg_api/__init__.py +0 -0
  87. {mreg_api-0.2.3 → mreg_api-0.4.0}/mreg_api/exceptions.py +0 -0
  88. /mreg_api-0.2.3/tests/__init__.py → /mreg_api-0.4.0/mreg_api/py.typed +0 -0
  89. {mreg_api-0.2.3 → mreg_api-0.4.0}/mreg_api/utilities/__init__.py +0 -0
  90. {mreg_api-0.2.3 → mreg_api-0.4.0}/mreg_api/utilities/fs.py +0 -0
  91. {mreg_api-0.2.3 → mreg_api-0.4.0}/mreg_api/utilities/shared.py +0 -0
  92. {mreg_api-0.2.3 → mreg_api-0.4.0}/mreg_api.egg-info/dependency_links.txt +0 -0
  93. {mreg_api-0.2.3 → mreg_api-0.4.0}/mreg_api.egg-info/top_level.txt +0 -0
  94. {mreg_api-0.2.3 → mreg_api-0.4.0}/setup.cfg +0 -0
  95. {mreg_api-0.2.3 → mreg_api-0.4.0}/tests/models/__init__.py +0 -0
  96. {mreg_api-0.2.3 → mreg_api-0.4.0}/tests/test_cache.py +0 -0
  97. {mreg_api-0.2.3 → mreg_api-0.4.0}/tests/test_types.py +0 -0
@@ -0,0 +1,29 @@
1
+ name: Documentation
2
+ on:
3
+ push:
4
+ branches:
5
+ - main
6
+ permissions:
7
+ contents: read
8
+ pages: write
9
+ id-token: write
10
+ jobs:
11
+ deploy:
12
+ environment:
13
+ name: github-pages
14
+ url: ${{ steps.deployment.outputs.page_url }}
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/configure-pages@v6
18
+ - uses: actions/checkout@v6
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v2
21
+ - name: Set up Python 3.12
22
+ run: uv python install 3.12
23
+ - run: uv sync --only-group docs
24
+ - run: uv run zensical build --clean
25
+ - uses: actions/upload-pages-artifact@v5
26
+ with:
27
+ path: site
28
+ - uses: actions/deploy-pages@v5
29
+ id: deployment
@@ -0,0 +1,52 @@
1
+ on:
2
+ push:
3
+ pull_request:
4
+
5
+ env:
6
+ UV_FROZEN: 1
7
+
8
+ name: Tests
9
+ jobs:
10
+ unit:
11
+ name: Unit tests
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ python-version:
17
+ - "3.11"
18
+ - "3.12"
19
+ - "3.13"
20
+ - "3.14"
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+ - name: Install uv
24
+ uses: astral-sh/setup-uv@v2
25
+ - name: Set up Python ${{ matrix.python-version }}
26
+ run: uv python install ${{ matrix.python-version }}
27
+ - name: Install dependencies
28
+ run: uv sync
29
+ - name: Run unit tests
30
+ run: bash ci/run_tests.sh --unit-only --no-cov
31
+
32
+ integration:
33
+ name: Integration tests
34
+ runs-on: ubuntu-latest
35
+ strategy:
36
+ fail-fast: false
37
+ matrix:
38
+ python-version:
39
+ - "3.11"
40
+ - "3.12"
41
+ - "3.13"
42
+ - "3.14"
43
+ steps:
44
+ - uses: actions/checkout@v4
45
+ - name: Install uv
46
+ uses: astral-sh/setup-uv@v2
47
+ - name: Set up Python ${{ matrix.python-version }}
48
+ run: uv python install ${{ matrix.python-version }}
49
+ - name: Install dependencies
50
+ run: uv sync
51
+ - name: Run integration tests
52
+ run: bash ci/run_tests.sh --integration-only --no-cov
@@ -110,3 +110,8 @@ venv.bak/
110
110
  mreg/.idea
111
111
 
112
112
  *.log
113
+
114
+ # Claude
115
+ .claude/settings.local.json
116
+ CLAUDE.local.md
117
+ !.claude/CLAUDE.md
@@ -5,5 +5,9 @@
5
5
  },
6
6
  "line-length": false,
7
7
  "no-bare-urls": false,
8
- "no-inline-html": false
8
+ "no-inline-html": false,
9
+ "MD046": false,
10
+ "MD056": {
11
+ "severity": "warning"
12
+ }
9
13
  }
@@ -7,6 +7,54 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  <!-- ## Unreleased -->
9
9
 
10
+ ## [0.4.0](https://github.com/unioslo/mreg-api/releases/tag/0.4.0) - 2026-08-24
11
+
12
+ ### Added
13
+
14
+ - `MregClient.community` property to access the `CommunityManager` for managing communities.
15
+ - Still exists via `MregClient.network.community`.
16
+ - `EventLog.get(subject=..., kind=..., level=..., min_level=...)` method to support retrieving events and filtering by a combination of subject, kind, and level. This replaces the existing `get_for`, `get_by_kind`, `get_by_level`, and `get_at_or_above` methods, and allows one to combine all their filters in a single call. Currently, only intersections of filters are supported (i.e. events must match all filters to be returned). Future releases may support unions of filters (i.e. events matching any filter will be returned).
17
+ - `MregClient.policy` property that groups the existing `MregClient.atom`, `MregClient.role`, `MregClient.label` into a common namespace as `MregClient.policy.role`, `MregClient.policy.atom`, and `MregClient.policy.label`.
18
+
19
+ ### Changed
20
+
21
+ - `MregClient.*.create()` semantics and return type changes:
22
+ - POST requests now prefer to return the created object found in the response, rather than fetching the object again from the API via the location header. The manager falls back on fetching via location header if the response does not contain the created object.
23
+ - `create()` method return type changed from `T | None` to `T`. Never returns None.
24
+ - `create(fetch_after_create)` parameter removed.
25
+ - `models.LDAPHealth` now defines `status` as a `Literal["OK", "Down", "Unknown"]` type instead of a generic string.
26
+ - `models.UserInfo` now has defaults for all its fields.
27
+ - `MregClient.community.create()` now returns the created `Community` object instead of a boolean indicating success.
28
+ - Default limit of 500 for `list()` calls has been changed to `None` (unrestricted).
29
+
30
+ ### Removed
31
+
32
+ - **BREAKING:** `create(fetch_after_create)` parameter. No longer has any meaning as each endpoint returns a valid object representation in the response body after creation.
33
+
34
+ ### Deprecated
35
+
36
+ - `EventLog.get_all()` -> `EventLog.get()`
37
+ - `EventLog.get_for()` -> `EventLog.get(subject=...)`
38
+ - `EventLog.get_by_kind()` -> `EventLog.get(kind=...)`
39
+ - `EventLog.get_by_level()` -> `EventLog.get(level=...)`
40
+ - `EventLog.get_at_or_above()` -> `EventLog.get(min_level=...)`
41
+
42
+ ## [0.3.0](https://github.com/unioslo/mreg-api/releases/tag/0.3.0) - 2026-08-12
43
+
44
+ ### Added
45
+
46
+ - `Host.get_community_associations(name: str)` method to retrieve all ipaddress->community associations for the host with the given community name.
47
+ - `Network.get_communities(name: str)` method to retrieve all communities with the given name.
48
+
49
+ ### Changed
50
+
51
+ - **BREAKING**: `MregClient` is no longer a singleton. Each instance is independent and maintains its own state. The `MregClient.get_instance()` method has been removed. Each instantiation of `MregClient` is now independent and maintains its own state, including history, events and cache.
52
+ - `Network.get_community()` now ignores case when searching for communities by name. It returns the first community with the given name, or None if not found.
53
+
54
+ ### Removed
55
+
56
+ - **BREAKING**: `MregClient.reset_instance` removed because the singleton pattern has been removed.
57
+
10
58
  ## [0.2.3](https://github.com/unioslo/mreg-api/releases/tag/0.2.3) - 2026-05-21
11
59
 
12
60
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mreg-api
3
- Version: 0.2.3
3
+ Version: 0.4.0
4
4
  Summary: MREG API
5
5
  Author-email: Terje Kvernes <terjekv@uio.no>
6
6
  Maintainer-email: Peder Hovdan Andresen <pederhan@uio.no>
@@ -692,7 +692,7 @@ Requires-Python: >=3.11
692
692
  Description-Content-Type: text/markdown
693
693
  License-File: LICENSE
694
694
  Requires-Dist: httpx[socks]>=0.28.1
695
- Requires-Dist: typing_extensions>=4.12.0
695
+ Requires-Dist: typing-extensions>=4.16.0
696
696
  Requires-Dist: pydantic>=2.12
697
697
  Requires-Dist: pydantic-extra-types>=2.1.0
698
698
  Requires-Dist: diskcache>=5.6.3
@@ -701,7 +701,28 @@ Dynamic: license-file
701
701
 
702
702
  # MREG API [![Build Status](https://github.com/unioslo/mreg-api/actions/workflows/test.yml/badge.svg)](https://github.com/unioslo/mreg-api/actions/workflows/test.yml)
703
703
 
704
- ## Pre-commit Hooks
704
+ `mreg-api` is a Python client library for the [MREG](https://github.com/unioslo/mreg)
705
+ REST API. It gives you a typed, ergonomic interface to every MREG
706
+ resource through a single client
707
+ object with scoped namespaces for each resource type.
708
+
709
+ It provides optional caching of resources, automatic FQDN expansion of hostnames, and a consistent interface for all resource types. The client is compatible with Python 3.11 and later.
710
+
711
+
712
+ ## Documentation
713
+
714
+ https://unioslo.github.io/mreg-api/
715
+
716
+ ## Development
717
+
718
+ Set up a development environment with `uv`:
719
+
720
+ ```bash
721
+ git clone git@github.com:unioslo/mreg-api.git
722
+ uv sync
723
+ ```
724
+
725
+ ### Pre-commit Hooks
705
726
 
706
727
  This project uses `prek` to manage pre-commit hooks for code quality and formatting. To set up the pre-commit hooks, run the following command:
707
728
 
@@ -0,0 +1,44 @@
1
+ # MREG API [![Build Status](https://github.com/unioslo/mreg-api/actions/workflows/test.yml/badge.svg)](https://github.com/unioslo/mreg-api/actions/workflows/test.yml)
2
+
3
+ `mreg-api` is a Python client library for the [MREG](https://github.com/unioslo/mreg)
4
+ REST API. It gives you a typed, ergonomic interface to every MREG
5
+ resource through a single client
6
+ object with scoped namespaces for each resource type.
7
+
8
+ It provides optional caching of resources, automatic FQDN expansion of hostnames, and a consistent interface for all resource types. The client is compatible with Python 3.11 and later.
9
+
10
+
11
+ ## Documentation
12
+
13
+ https://unioslo.github.io/mreg-api/
14
+
15
+ ## Development
16
+
17
+ Set up a development environment with `uv`:
18
+
19
+ ```bash
20
+ git clone git@github.com:unioslo/mreg-api.git
21
+ uv sync
22
+ ```
23
+
24
+ ### Pre-commit Hooks
25
+
26
+ This project uses `prek` to manage pre-commit hooks for code quality and formatting. To set up the pre-commit hooks, run the following command:
27
+
28
+ ```bash
29
+ uv tool install prek
30
+ ```
31
+
32
+ Then, install the pre-commit hooks with:
33
+
34
+ ```bash
35
+ prek install
36
+ ```
37
+
38
+ Run the pre-commit checks manually with:
39
+
40
+ ```bash
41
+ prek run
42
+ # Optionally for all files:
43
+ prek run --all-files
44
+ ```
@@ -0,0 +1,152 @@
1
+ # Testing
2
+
3
+ mreg-api has two types of tests:
4
+
5
+ - **Unit tests** — Unit testing of functions and methods used by the package. No interaction with a live mreg server is required.
6
+ - **Integration tests** — Run against a live (containerized) mreg server. Skipped automatically when no server URL is configured.
7
+
8
+ ## Unit tests
9
+
10
+ Unit tests use `pytest-httpserver` to mock MREG server responses for any tests that require HTTP interactions. No external services required.
11
+
12
+ Bare `pytest` invocations ignore integration tests (skipped automatically when no server URL is configured):
13
+
14
+ ```bash
15
+ uv run pytest
16
+ ```
17
+
18
+ In case some configuration defines `MREG_URL`, unit tests can still be run exclusively by ignoring the integration tests via markers:
19
+
20
+ ```bash
21
+ uv run pytest -m "not integration"
22
+ ```
23
+
24
+ ## Integration tests
25
+
26
+ Integration tests run against a live mreg server. They are **skipped automatically** when no server URL is configured, so `uv run pytest` never fails due to a missing server.
27
+
28
+ ### Prerequisites
29
+
30
+ A running mreg instance (and PostgreSQL). The fastest way is the containerized setup:
31
+
32
+ ```bash
33
+ bash ci/run_tests.sh
34
+
35
+ # If port 8000 is already in use (e.g., a local mreg dev server):
36
+ MREG_PORT=8081 bash ci/run_tests.sh
37
+ ```
38
+
39
+ This starts containers, creates a superuser, then runs both unit and integration tests with combined coverage output in `htmlcov/`.
40
+
41
+ Flags:
42
+
43
+ | Flag | Effect |
44
+ |---|---|
45
+ | `--unit-only` | Skip integration tests (no containers started) |
46
+ | `--integration-only` | Skip unit tests |
47
+ | `--no-cov` | Disable coverage. Used in CI |
48
+ | `-h`, `--help` | Show usage |
49
+ | `-- ARGS...` | Pass everything after `--` to pytest |
50
+
51
+ Unknown flags are an error (exit code 2) — the script never silently ignores them.
52
+
53
+ Container setup (`ci/run_tests.sh` only, not read by pytest):
54
+
55
+ | Env var | Default | Description |
56
+ |---|---|---|
57
+ | `MREG_IMAGE` | `ghcr.io/unioslo/mreg:master` | mreg container image |
58
+ | `MREG_IMAGE_PULL_POLICY` | `missing` | Compose pull policy: `missing`, `always`, `never` |
59
+ | `MREG_PORT` | `8000` | Host port the mreg container is exposed on |
60
+
61
+ `master` is a mutable tag, so a local image can go stale. Use `MREG_IMAGE_PULL_POLICY=always` to pull the current one.
62
+
63
+
64
+
65
+ ### Running manually against a local server
66
+
67
+ ```bash
68
+ # Minimal — username/password default to "test"
69
+ MREG_URL=http://127.0.0.1:8000 uv run pytest tests/integration/
70
+
71
+ # All options explicit
72
+ MREG_URL=http://127.0.0.1:8000 \
73
+ MREG_USERNAME=myuser \
74
+ MREG_PASSWORD=secret \
75
+ MREG_DOMAIN=example.com \
76
+ MREG_CACHE=0 \
77
+ MREG_TEST_NETWORK=10.0.0.0/8 \
78
+ uv run pytest tests/integration/ -v
79
+
80
+ # Via CLI flags instead of env vars
81
+ uv run pytest tests/integration/ \
82
+ --mreg-url http://127.0.0.1:8000 \
83
+ --mreg-username myuser \
84
+ --mreg-password secret
85
+ ```
86
+
87
+ ### Configuration
88
+
89
+ | Env var | CLI flag | Default | Description |
90
+ |---|---|---|---|
91
+ | `MREG_URL` | `--mreg-url` | *(none — required to activate integration tests)* | mreg server base URL |
92
+ | `MREG_USERNAME` | `--mreg-username` | `test` | Login username |
93
+ | `MREG_PASSWORD` | `--mreg-password` | `test` | Login password |
94
+ | `MREG_DOMAIN` | `--mreg-domain` | `example.com` | Default domain for the client. Controls the main zone used in integration tests |
95
+ | `MREG_CACHE` | `--mreg-cache` | `false` | Enable mreg client cache (`1`/`true`/`yes` = on) |
96
+ | `MREG_TEST_NETWORK` | `--test-network` | `10.0.0.0/8` | Network CIDR created as shared test network |
97
+ | `MREG_TEST_IP` | `--test-ip` | `10.0.0.1` | IP address used in IP-related tests |
98
+
99
+ CLI flags take precedence over env vars.
100
+
101
+
102
+ ### Subset and extra arguments
103
+
104
+
105
+ ```bash
106
+ # Fast run without coverage
107
+ bash ci/run_tests.sh --no-cov
108
+
109
+ # Single module
110
+ bash ci/run_tests.sh --integration-only -- tests/integration/test_labels.py
111
+
112
+ # Stop on first failure, filter by name
113
+ bash ci/run_tests.sh --integration-only -- -x -k labels
114
+ ```
115
+
116
+ Each pass selects its tests by marker (`-m integration` / `-m "not integration"`) rather than by
117
+ path, so a path after `--` narrows the run. When both suites run, the arguments go to both pytest
118
+ invocations.
119
+
120
+ ## Coverage
121
+
122
+ ### Unit tests only
123
+
124
+ ```bash
125
+ uv run pytest tests/ --ignore=tests/integration \
126
+ --cov=mreg_api --cov-report=term-missing
127
+ ```
128
+
129
+ ### Combined unit + integration
130
+
131
+ Run `ci/run_tests.sh` (uses `--cov-append` to merge both passes), or manually:
132
+
133
+ ```bash
134
+ # Pass 1: unit tests — write .coverage
135
+ uv run pytest tests/ --ignore=tests/integration \
136
+ --cov=mreg_api --cov-report=
137
+
138
+ # Pass 2: integration tests — append to .coverage
139
+ MREG_URL=http://127.0.0.1:8000 MREG_USERNAME=test MREG_PASSWORD=test \
140
+ uv run pytest tests/integration/ \
141
+ --cov=mreg_api --cov-append --cov-report=html --cov-report=term
142
+
143
+ # Open report
144
+ open htmlcov/index.html
145
+ ```
146
+
147
+ ## Markers
148
+
149
+ | Marker | Meaning |
150
+ |---|---|
151
+ | `integration` | Requires live mreg server — skipped without `MREG_URL` |
152
+ | `readonly` | Test only reads data, safe against staging/production servers |
@@ -0,0 +1,15 @@
1
+ FROM ubuntu:latest
2
+
3
+ RUN apt-get update && apt-get install -y --no-install-recommends \
4
+ curl \
5
+ git \
6
+ && rm -rf /var/lib/apt/lists/*
7
+
8
+ RUN curl -LsSf https://astral.sh/uv/install.sh | sh
9
+ ENV PATH="/root/.local/bin:$PATH"
10
+
11
+ RUN uv python install 3.12
12
+
13
+ WORKDIR /build
14
+ COPY . .
15
+ RUN uv sync -q
@@ -0,0 +1,40 @@
1
+ services:
2
+ postgres:
3
+ image: postgres
4
+ environment:
5
+ POSTGRES_USER: mreg
6
+ POSTGRES_DB: mreg
7
+ POSTGRES_PASSWORD: mreg
8
+ healthcheck:
9
+ test: ["CMD", "pg_isready", "--username=mreg"]
10
+ interval: 5s
11
+ timeout: 5s
12
+ retries: 10
13
+
14
+ mreg:
15
+ image: ${MREG_IMAGE:-ghcr.io/unioslo/mreg:master}
16
+ pull_policy: ${MREG_IMAGE_PULL_POLICY:-missing}
17
+ container_name: mreg
18
+ depends_on:
19
+ postgres:
20
+ condition: service_healthy
21
+ ports:
22
+ - "${MREG_PORT:-8000}:8000"
23
+ environment:
24
+ MREG_DB_HOST: postgres
25
+ MREG_DB_NAME: mreg
26
+ MREG_DB_USER: mreg
27
+ MREG_DB_PASSWORD: mreg
28
+ MREG_MAP_GLOBAL_COMMUNITY_NAMES: "1"
29
+ CI: "yes"
30
+ healthcheck:
31
+ test: ["CMD", "test", "-f", "/var/run/gunicorn.pid"]
32
+ interval: 5s
33
+ timeout: 5s
34
+ retries: 20
35
+
36
+ wait:
37
+ image: hello-world
38
+ depends_on:
39
+ mreg:
40
+ condition: service_healthy
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ REPO_ROOT="$(dirname "$SCRIPT_DIR")"
6
+
7
+ RUN_UNIT=true
8
+ RUN_INTEGRATION=true
9
+ RUN_COVERAGE=true
10
+ MREG_IMAGE="${MREG_IMAGE:-ghcr.io/unioslo/mreg:master}"
11
+ MREG_IMAGE_PULL_POLICY="${MREG_IMAGE_PULL_POLICY:-missing}"
12
+ # Host port the mreg container is exposed on. Override if 8000 is already in use.
13
+ MREG_PORT="${MREG_PORT:-8000}"
14
+ MREG_URL="${MREG_URL:-http://127.0.0.1:${MREG_PORT}}"
15
+ MREG_USERNAME="${MREG_USERNAME:-test}"
16
+ MREG_PASSWORD="${MREG_PASSWORD:-test}"
17
+ MREG_CACHE="${MREG_CACHE:-0}"
18
+
19
+ usage() {
20
+ cat <<'EOF'
21
+ Usage: bash ci/run_tests.sh [--unit-only|--integration-only] [--no-cov] [-- pytest args...]
22
+
23
+ Everything after -- is passed to pytest:
24
+
25
+ bash ci/run_tests.sh --integration-only -- tests/integration/test_labels.py
26
+ bash ci/run_tests.sh --integration-only -- -x -k labels
27
+ EOF
28
+ }
29
+
30
+ # Extra arguments passed to pytest (everything after --).
31
+ EXTRA_ARGS=()
32
+
33
+ while [[ $# -gt 0 ]]; do
34
+ case $1 in
35
+ --unit-only) RUN_INTEGRATION=false; shift ;;
36
+ --integration-only) RUN_UNIT=false; shift ;;
37
+ --no-cov) RUN_COVERAGE=false; shift ;;
38
+ -h|--help) usage; exit 0 ;;
39
+ --) shift; EXTRA_ARGS=("$@"); break ;;
40
+ *)
41
+ echo "ERROR: unknown option: $1" >&2
42
+ usage >&2
43
+ exit 2
44
+ ;;
45
+ esac
46
+ done
47
+
48
+ if command -v podman &>/dev/null && ! command -v docker &>/dev/null; then
49
+ DOCKER=podman
50
+ else
51
+ DOCKER=docker
52
+ fi
53
+
54
+ if [[ "$RUN_INTEGRATION" == "true" ]]; then
55
+ # Check if port is already in use before starting containers
56
+ if lsof -i ":${MREG_PORT}" 2>/dev/null | grep -q LISTEN; then
57
+ echo "ERROR: Port ${MREG_PORT} is already in use." >&2
58
+ echo "Set MREG_PORT to a free port: MREG_PORT=8081 bash ci/run_tests.sh" >&2
59
+ exit 1
60
+ fi
61
+
62
+ cleanup() {
63
+ cd "$SCRIPT_DIR"
64
+ $DOCKER compose down --remove-orphans 2>/dev/null || true
65
+ }
66
+ trap cleanup EXIT
67
+
68
+ cd "$SCRIPT_DIR"
69
+
70
+ echo "Starting mreg and postgres on port ${MREG_PORT}..."
71
+ MREG_IMAGE="$MREG_IMAGE" MREG_PORT="$MREG_PORT" \
72
+ MREG_IMAGE_PULL_POLICY="$MREG_IMAGE_PULL_POLICY" \
73
+ $DOCKER compose up -d
74
+
75
+ echo "Creating superuser..."
76
+ $DOCKER exec mreg uv run /app/manage.py create_mreg_superuser \
77
+ --username "$MREG_USERNAME" --password "$MREG_PASSWORD" 2>/dev/null \
78
+ || $DOCKER exec mreg /app/manage.py create_mreg_superuser \
79
+ --username "$MREG_USERNAME" --password "$MREG_PASSWORD"
80
+
81
+
82
+ fi
83
+
84
+ cd "$REPO_ROOT"
85
+
86
+ UNIT_EXIT=0
87
+ INTEGRATION_EXIT=0
88
+
89
+ if [[ "$RUN_UNIT" == "true" ]]; then
90
+ echo "Running unit tests..."
91
+ # No path here: pytest falls back to testpaths (tests/) from pyproject.toml,
92
+ # so a path in EXTRA_ARGS narrows the run instead of being added to it.
93
+ UNIT_ARGS=(-m "not integration" -v)
94
+ if [[ "$RUN_COVERAGE" == "true" ]]; then
95
+ UNIT_ARGS+=(--cov=mreg_api --cov-report=)
96
+ else
97
+ UNIT_ARGS+=(--no-cov)
98
+ fi
99
+ UNIT_ARGS+=(${EXTRA_ARGS[@]+"${EXTRA_ARGS[@]}"})
100
+ uv run pytest "${UNIT_ARGS[@]}" || UNIT_EXIT=$?
101
+ fi
102
+
103
+ if [[ "$RUN_INTEGRATION" == "true" ]]; then
104
+ echo "Running integration tests..."
105
+ INTEGRATION_ARGS=(-m integration -v)
106
+ if [[ "$RUN_COVERAGE" == "true" ]]; then
107
+ INTEGRATION_ARGS+=(--cov=mreg_api --cov-append --cov-report=html --cov-report=term)
108
+ else
109
+ INTEGRATION_ARGS+=(--no-cov)
110
+ fi
111
+ INTEGRATION_ARGS+=(${EXTRA_ARGS[@]+"${EXTRA_ARGS[@]}"})
112
+ MREG_URL="$MREG_URL" \
113
+ MREG_USERNAME="$MREG_USERNAME" \
114
+ MREG_PASSWORD="$MREG_PASSWORD" \
115
+ uv run pytest "${INTEGRATION_ARGS[@]}" || INTEGRATION_EXIT=$?
116
+ fi
117
+
118
+ if [[ $UNIT_EXIT -ne 0 || $INTEGRATION_EXIT -ne 0 ]]; then
119
+ exit 1
120
+ fi
121
+ exit 0
@@ -0,0 +1,62 @@
1
+ ---
2
+ icon: lucide/key-round
3
+ ---
4
+
5
+ # Authentication
6
+
7
+ The client does not authenticate on construction and never reads credentials from the
8
+ environment or a token file on its own — authentication is always explicit. There are two
9
+ ways to authenticate.
10
+
11
+ ## Username and password
12
+
13
+ [`login()`][mreg_api.client.MregClient.login] exchanges credentials for a token, stores
14
+ it on the client, and returns it. Reading credentials from the environment is a common
15
+ pattern:
16
+
17
+ ```python
18
+ import os
19
+ from mreg_api import MregClient
20
+
21
+ client = MregClient(url="https://mreg.example.com", domain="example.com")
22
+ client.login(
23
+ username=os.environ["MREG_USERNAME"],
24
+ password=os.environ["MREG_PASSWORD"],
25
+ )
26
+ ```
27
+
28
+ `login()` raises [`LoginFailedError`][mreg_api.exceptions.LoginFailedError] on a
29
+ connection failure or bad credentials.
30
+
31
+ ## Existing token
32
+
33
+ !!! note
34
+
35
+ Token auth is primarily relevant to persistent sessions for applications such as [mreg-cli](https://github.com/unioslo/mreg-cli), not for scripts or ephemeral sessions.
36
+
37
+ If you already have a token, set it directly with
38
+ [`set_token()`][mreg_api.client.MregClient.set_token] — no `login()` call needed:
39
+
40
+ ```python
41
+ client = MregClient(url="https://mreg.example.com")
42
+ client.set_token(os.environ["MREG_TOKEN"])
43
+ ```
44
+
45
+ ## Managing the token
46
+
47
+ ```python
48
+ client.get_token() # current token, or None
49
+ client.test_auth() # raises InvalidAuthTokenError if the token is invalid
50
+ client.unset_token() # clear the token locally
51
+ client.logout() # invalidate the token on the server
52
+ ```
53
+
54
+ ## Correlation IDs
55
+
56
+ To make requests easier to trace in server logs, attach a correlation ID that is sent
57
+ with every subsequent request:
58
+
59
+ ```python
60
+ client.set_correlation_id("nightly-sync")
61
+ client.get_correlation_id()
62
+ ```