test(integration): added integration tests

This commit is contained in:
ITQ
2026-02-23 11:48:03 +03:00
parent 3003ac888b
commit 15d80532e0
8 changed files with 1456 additions and 0 deletions
@@ -0,0 +1,231 @@
import json
import uuid
from decimal import Decimal
from typing import override
from django.core.cache import cache
from django.test import Client, TestCase
from django.urls import reverse
from django.utils import timezone
from apps.events.tests.helpers import make_event_type, make_exposure_type
from apps.experiments.services import (
experiment_approve,
experiment_start,
experiment_submit_for_review,
)
from apps.experiments.tests.helpers import add_two_variants, make_experiment
from apps.metrics.services import (
experiment_metric_add,
metric_definition_create,
)
from apps.reviews.services import review_settings_update
from apps.reviews.tests.helpers import make_approver, make_experimenter
class APIContractFlowTest(TestCase):
@override
def setUp(self) -> None:
cache.clear()
self.client = Client()
review_settings_update(
default_min_approvals=1,
allow_any_approver=True,
)
owner = make_experimenter("_api")
approver = make_approver("_api")
self.experiment = make_experiment(
owner=owner,
suffix="_api",
traffic_allocation=Decimal("100.00"),
)
add_two_variants(self.experiment)
self.metric = metric_definition_create(
key="ctr_api",
name="CTR",
metric_type="ratio",
direction="higher_is_better",
calculation_rule={
"numerator_event": "api_click",
"denominator_event": "api_exposure",
},
)
experiment_metric_add(
experiment=self.experiment,
metric=self.metric,
is_primary=True,
)
make_exposure_type(name="api_exposure")
make_event_type(
name="api_click",
display_name="Click",
requires_exposure=True,
)
self.experiment = experiment_submit_for_review(
experiment=self.experiment, user=owner
)
self.experiment = experiment_approve(
experiment=self.experiment, approver=approver
)
self.experiment = experiment_start(
experiment=self.experiment, user=owner
)
def test_decide_to_events_to_report_via_http(self) -> None:
decide_resp = self.client.post(
reverse("api-1:decide"),
data=json.dumps(
{
"subject_id": "api_user_1",
"flags": [self.experiment.flag.key],
"subject_attributes": {},
}
),
content_type="application/json",
)
self.assertEqual(decide_resp.status_code, 200)
decide_data = decide_resp.json()
self.assertEqual(len(decide_data["decisions"]), 1)
decision = decide_data["decisions"][0]
self.assertEqual(decision["reason"], "experiment_assigned")
self.assertIsNotNone(decision["decision_id"])
self.assertIsNotNone(decision["variant_id"])
now = timezone.now().isoformat()
events_resp = self.client.post(
reverse("api-1:ingest_events"),
data=json.dumps(
{
"events": [
{
"event_id": "api_exp_1",
"event_type": "api_exposure",
"decision_id": decision["decision_id"],
"subject_id": "api_user_1",
"timestamp": now,
"properties": {},
},
{
"event_id": "api_click_1",
"event_type": "api_click",
"decision_id": decision["decision_id"],
"subject_id": "api_user_1",
"timestamp": now,
"properties": {},
},
]
}
),
content_type="application/json",
)
self.assertEqual(events_resp.status_code, 200)
events_data = events_resp.json()
self.assertEqual(events_data["accepted"], 2)
self.assertEqual(events_data["rejected"], 0)
self.assertEqual(events_data["duplicates"], 0)
report_resp = self.client.get(
reverse(
"api-1:get_experiment_report",
args=[self.experiment.pk],
),
)
self.assertEqual(report_resp.status_code, 200)
report = report_resp.json()
self.assertEqual(report["experiment_id"], str(self.experiment.pk))
total_exposures = sum(v["exposures"] for v in report["variants"])
self.assertEqual(total_exposures, 1)
self.assertEqual(len(report["variants"]), 2)
for variant in report["variants"]:
if variant["exposures"] > 0:
self.assertEqual(len(variant["metrics"]), 1)
self.assertEqual(
variant["metrics"][0]["metric_key"], "ctr_api"
)
def test_decide_returns_all_requested_flags(self) -> None:
resp = self.client.post(
reverse("api-1:decide"),
data=json.dumps(
{
"subject_id": "api_user_2",
"flags": [
self.experiment.flag.key,
"nonexistent_flag",
],
}
),
content_type="application/json",
)
self.assertEqual(resp.status_code, 200)
data = resp.json()
self.assertEqual(len(data["decisions"]), 2)
reasons = {d["flag"]: d["reason"] for d in data["decisions"]}
self.assertEqual(
reasons[self.experiment.flag.key], "experiment_assigned"
)
self.assertEqual(reasons["nonexistent_flag"], "flag_not_found")
def test_events_dedup_via_http(self) -> None:
cache.clear()
decide_resp = self.client.post(
reverse("api-1:decide"),
data=json.dumps(
{
"subject_id": "api_user_3",
"flags": [self.experiment.flag.key],
}
),
content_type="application/json",
)
decision = decide_resp.json()["decisions"][0]
now = timezone.now().isoformat()
event_payload = json.dumps(
{
"events": [
{
"event_id": "api_ddup_1",
"event_type": "api_exposure",
"decision_id": decision["decision_id"],
"subject_id": "api_user_3",
"timestamp": now,
"properties": {},
}
]
}
)
r1 = self.client.post(
reverse("api-1:ingest_events"),
data=event_payload,
content_type="application/json",
)
self.assertEqual(r1.json()["accepted"], 1)
r2 = self.client.post(
reverse("api-1:ingest_events"),
data=event_payload,
content_type="application/json",
)
self.assertEqual(r2.json()["duplicates"], 1)
self.assertEqual(r2.json()["accepted"], 0)
def test_report_404_for_unknown_experiment(self) -> None:
resp = self.client.get(
reverse(
"api-1:get_experiment_report",
args=[uuid.uuid4()],
),
)
self.assertEqual(resp.status_code, 404)