Skip to content

Commit

Permalink
Fix #105: Include checks about normandy-recipes-capabilities
Browse files Browse the repository at this point in the history
  • Loading branch information
leplatrem committed Oct 21, 2019
1 parent 637794e commit d7fc082
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 20 deletions.
47 changes: 32 additions & 15 deletions checks/normandy/remotesettings_recipes.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,52 @@
"""
The recipes in the Remote Settings collection should match the Normandy API.
The lists of missing and extraneous recipes are returned.
The recipes in the Remote Settings collection should match the Normandy API. The
collection of recipes with capabilities should contain all baseline recipes.
The lists of missing and extraneous recipes are returned, as well the list of
inconsistencies between the baseline and capabilities collections.
"""
from poucave.typings import CheckResult
from poucave.utils import fetch_json

NORMANDY_URL = "{server}/api/v1/recipe/signed/?enabled=1"
REMOTESETTINGS_URL = "{server}/buckets/main/collections/normandy-recipes/records"
REMOTESETTINGS_URL = "{server}/buckets/main/collections/{cid}/records"


async def run(normandy_server: str, remotesettings_server: str) -> CheckResult:
# Recipes from source of truth.
normandy_url = NORMANDY_URL.format(server=normandy_server)
normandy_recipes = await fetch_json(normandy_url)
normandy_by_id = {r["recipe"]["id"]: r["recipe"] for r in normandy_recipes}

# Recipes published on Remote Settings.
remotesettings_url = REMOTESETTINGS_URL.format(server=remotesettings_server)
body = await fetch_json(remotesettings_url)
remotesettings_recipes = body["data"]
# Baseline recipes published on Remote Settings.
rs_recipes = REMOTESETTINGS_URL.format(
server=remotesettings_server, cid="normandy-recipes"
)
body = await fetch_json(rs_recipes)
rs_recipes = body["data"]
rs_baseline_by_id = {r["recipe"]["id"]: r["recipe"] for r in rs_recipes}

remotesettings_by_id = {
r["recipe"]["id"]: r["recipe"] for r in remotesettings_recipes
}
normandy_by_id = {r["recipe"]["id"]: r["recipe"] for r in normandy_recipes}
# Recipes with advanced capabilities.
rs_recipes_caps = REMOTESETTINGS_URL.format(
server=remotesettings_server, cid="normandy-recipes-capabilities"
)
body = await fetch_json(rs_recipes_caps)
rs_recipes_caps = body["data"]
rs_capabilities_by_id = {r["recipe"]["id"]: r["recipe"] for r in rs_recipes_caps}

# Make sure the baseline recipes are all listed in the capabilites collection.
missing_caps = []
for rid, r in rs_baseline_by_id.items():
if rid not in rs_capabilities_by_id:
missing_caps.append({"id": r["id"], "name": r["name"]})

# Make sure the baseline recipes are all listed in the baseline collection
missing = []
for rid, r in normandy_by_id.items():
published = remotesettings_by_id.pop(rid, None)
published = rs_baseline_by_id.pop(rid, None)
if published is None:
missing.append({"id": r["id"], "name": r["name"]})
extras = [{"id": r["id"], "name": r["name"]} for r in remotesettings_by_id.values()]
extras = [{"id": r["id"], "name": r["name"]} for r in rs_baseline_by_id.values()]

ok = (len(missing) + len(extras)) == 0
return ok, {"missing": missing, "extras": extras}
ok = (len(missing_caps) + len(missing) + len(extras)) == 0
return ok, {"inconsistent": missing_caps, "missing": missing, "extras": extras}
33 changes: 28 additions & 5 deletions tests/checks/normandy/test_remotesettings_recipes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

NORMANDY_SERVER = "http://n"
REMOTESETTINGS_SERVER = "http://rs/v1"
REMOTESETTINGS_URL = (
REMOTESETTINGS_BASELINE_URL = (
REMOTESETTINGS_SERVER + "/buckets/main/collections/normandy-recipes/records"
)
REMOTESETTINGS_CAPABILITIES_URL = (
REMOTESETTINGS_SERVER
+ "/buckets/main/collections/normandy-recipes-capabilities/records"
)

NORMANDY_RECIPE = {
"signature": {
Expand Down Expand Up @@ -41,32 +45,51 @@
},
}

REMOTESETTINGS_RECIPE_WITH_CAPS = {
"id": "314",
"recipe": {
"id": 314,
"name": f"With caps",
"capabilities": ["action.preference-experiment"],
},
}


async def test_positive(mock_aioresponses):
mock_aioresponses.get(
NORMANDY_URL.format(server=NORMANDY_SERVER), payload=[NORMANDY_RECIPE]
)
mock_aioresponses.get(REMOTESETTINGS_URL, payload={"data": [REMOTESETTINGS_RECIPE]})
mock_aioresponses.get(
REMOTESETTINGS_BASELINE_URL, payload={"data": [REMOTESETTINGS_RECIPE]}
)
mock_aioresponses.get(
REMOTESETTINGS_CAPABILITIES_URL,
payload={"data": [REMOTESETTINGS_RECIPE, REMOTESETTINGS_RECIPE_WITH_CAPS]},
)

status, data = await run(NORMANDY_SERVER, REMOTESETTINGS_SERVER)

assert status is True
assert data == {"missing": [], "extras": []}
assert data == {"inconsistent": [], "missing": [], "extras": []}


async def test_negative(mock_aioresponses):
mock_aioresponses.get(
NORMANDY_URL.format(server=NORMANDY_SERVER), payload=[NORMANDY_RECIPE]
)
mock_aioresponses.get(
REMOTESETTINGS_URL,
REMOTESETTINGS_BASELINE_URL,
payload={"data": [{"id": "42", "recipe": {"id": 42, "name": "Extra"}}]},
)

mock_aioresponses.get(
REMOTESETTINGS_CAPABILITIES_URL,
payload={"data": [REMOTESETTINGS_RECIPE_WITH_CAPS]},
)
status, data = await run(NORMANDY_SERVER, REMOTESETTINGS_SERVER)

assert status is False
assert data == {
"inconsistent": [{"id": 42, "name": "Extra"}],
"missing": [{"id": 829, "name": "Mobile Browser usage"}],
"extras": [{"id": 42, "name": "Extra"}],
}

0 comments on commit d7fc082

Please sign in to comment.