Skip to content

Commit

Permalink
Merge pull request #5 from numberly/tests/metrics
Browse files Browse the repository at this point in the history
test(metrics): initial support
  • Loading branch information
Solvik authored Feb 2, 2025
2 parents 6c0a11b + 88f575b commit c41e6dd
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 32 deletions.
35 changes: 35 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
default_language_version:
python: python3.12

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace # Trims trailing whitespace.
- id: end-of-file-fixer # Makes sure files end in a newline and only a newline.
- id: check-added-large-files # Prevent giant files from being committed.
- id: requirements-txt-fixer
- id: check-merge-conflict

- repo: https://github.com/commitizen-tools/commitizen
rev: v3.5.3
hooks:
- id: commitizen
stages:
- commit-msg

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.1
hooks:
- id: ruff
args: [--fix]
- id: ruff-format

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.13.0
hooks:
- id: mypy
entry: mypy
files: send/
language: system
args: [--install-types, --non-interactive]
1 change: 0 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,3 @@ USER nobody:nogroup
EXPOSE 8000

CMD ["python", "typesense_exporter.py", "--port=8000"]

1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,3 @@ This design guarantees that metrics are always up-to-date at scrape time (with n
## License

This exporter is released under the MIT License. See LICENSE for details (or replace with your preferred license).

3 changes: 2 additions & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mypy>=1.14
pytest>=7.0
pytest-asyncio>=0.23.5
pytest-cov>=4.1
pytest>=7.0
requests-mock==1.12.1
types-requests>=2.32
89 changes: 89 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import pytest
from typesense_exporter import TypesenseCollector


@pytest.fixture
def response_metrics_json():
yield {
"system_cpu1_active_percentage": "9.09",
"system_cpu2_active_percentage": "0.00",
"system_cpu3_active_percentage": "0.00",
"system_cpu4_active_percentage": "0.00",
"system_cpu_active_percentage": "0.00",
"system_disk_total_bytes": "102888095744",
"system_disk_used_bytes": "4177268736",
"system_memory_total_bytes": "16764186624",
"system_memory_total_swap_bytes": "0",
"system_memory_used_bytes": "3234148352",
"system_memory_used_swap_bytes": "0",
"system_network_received_bytes": "6534814741",
"system_network_sent_bytes": "4613106962",
"typesense_memory_active_bytes": "51126272",
"typesense_memory_allocated_bytes": "43065104",
"typesense_memory_fragmentation_ratio": "0.16",
"typesense_memory_mapped_bytes": "97370112",
"typesense_memory_metadata_bytes": "9009280",
"typesense_memory_resident_bytes": "51126272",
"typesense_memory_retained_bytes": "30556160",
}


@pytest.fixture
def response_stats_json():
yield {
"delete_latency_ms": 0,
"delete_requests_per_second": 0,
"import_latency_ms": 0,
"import_requests_per_second": 0,
"latency_ms": {"GET /health": 0.0, "GET /status": 0.0},
"overloaded_requests_per_second": 0,
"pending_write_batches": 0,
"requests_per_second": {"GET /health": 1.5, "GET /status": 0.6},
"search_latency_ms": 0,
"search_requests_per_second": 0,
"total_requests_per_second": 2.1,
"write_latency_ms": 0,
"write_requests_per_second": 0,
}


@pytest.fixture
def response_debug_json():
yield {"state": 1, "version": "0.24.0"}


@pytest.fixture
def response_collections_json():
yield [
{"name": "products", "num_documents": 100},
{"name": "users", "num_documents": 50},
]


@pytest.fixture(autouse=True)
def mock_typesense_api(
requests_mock,
response_metrics_json,
response_stats_json,
response_debug_json,
response_collections_json,
):
base_url = "http://localhost:8108"

requests_mock.get(f"{base_url}/metrics.json", json=response_metrics_json)
requests_mock.get(f"{base_url}/stats.json", json=response_stats_json)
requests_mock.get(f"{base_url}/debug", json=response_debug_json)
requests_mock.get(f"{base_url}/collections", json=response_collections_json)

yield requests_mock


@pytest.fixture
def typesense_collector():
return TypesenseCollector(
typesense_api_key="123",
metrics_url="http://localhost:8108/metrics.json",
stats_url="http://localhost:8108/stats.json",
debug_url="http://localhost:8108/debug",
nodes=[{"host": "localhost", "port": "8108", "protocol": "http"}],
)
101 changes: 72 additions & 29 deletions tests/test_typesense_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,79 @@
from typesense_exporter import parse_nodes_from_str


@pytest.mark.parametrize("test_input,expected,protocol,description", [
(
"localhost:8108",
[{"host": "localhost", "port": "8108", "protocol": "https"}],
"https",
"single node with port"
),
(
"host1:8108,host2:8108",
[
{"host": "host1", "port": "8108", "protocol": "https"},
{"host": "host2", "port": "8108", "protocol": "https"}
],
"https",
"multiple nodes"
),
(
"localhost",
[{"host": "localhost", "port": "8108", "protocol": "https"}],
"https",
"default port"
),
(
"localhost:8108",
[{"host": "localhost", "port": "8108", "protocol": "http"}],
"http",
"custom protocol"
),
])
@pytest.mark.parametrize(
"test_input,expected,protocol,description",
[
(
"localhost:8108",
[{"host": "localhost", "port": "8108", "protocol": "https"}],
"https",
"single node with port",
),
(
"host1:8108,host2:8108",
[
{"host": "host1", "port": "8108", "protocol": "https"},
{"host": "host2", "port": "8108", "protocol": "https"},
],
"https",
"multiple nodes",
),
(
"localhost",
[{"host": "localhost", "port": "8108", "protocol": "https"}],
"https",
"default port",
),
(
"localhost:8108",
[{"host": "localhost", "port": "8108", "protocol": "http"}],
"http",
"custom protocol",
),
],
)
def test_parse_nodes_from_str(test_input, expected, protocol, description):
nodes = parse_nodes_from_str(test_input, default_protocol=protocol)
assert len(nodes) == len(expected)
assert nodes == expected


def test_typesense_collector_metrics(typesense_collector):
metrics = list(typesense_collector._collect_metrics_json())
assert len(metrics) == 20


def test_typesense_collector_stats(typesense_collector):
metrics = list(typesense_collector._collect_stats_json())
assert len(metrics) == 13


def test_typesense_collector_json(typesense_collector):
metrics = list(typesense_collector._collect_debug_json())
assert len(metrics) == 1


def test_typesense_collector_collections(typesense_collector):
metrics = list(typesense_collector._collect_collections())
assert len(metrics) == 1

metric_family = metrics[0]
assert metric_family.name == "typesense_collection_documents"
assert (
metric_family.documentation
== "Number of documents in each Typesense collection"
)

assert len(metric_family.samples) == 2

sample_1 = metric_family.samples[0]
sample_2 = metric_family.samples[1]

assert sample_1.name == "typesense_collection_documents"
assert sample_1.labels == {"collection_name": "products"}
assert sample_1.value == 100.0

assert sample_2.name == "typesense_collection_documents"
assert sample_2.labels == {"collection_name": "users"}
assert sample_2.value == 50.0

0 comments on commit c41e6dd

Please sign in to comment.