Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/creative_agent/data/standard_formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,23 @@ def create_impression_tracker_asset() -> LibAssets:
)


def create_click_tracker_asset() -> LibAssets:
"""Create an optional click tracker asset for 3rd party tracking.

This creates a URL asset with url_type='tracker_redirect' that can be used
for third-party click tracking redirects.
"""
return create_asset(
asset_id="click_tracker",
asset_type=AssetType.url,
required=False,
requirements={
"url_type": "tracker_redirect",
"description": "3rd party click tracking redirect URL",
},
)


def create_fixed_render(width: int, height: int, role: str = "primary") -> LibRender:
"""Create a render with fixed dimensions (non-responsive).

Expand Down Expand Up @@ -156,6 +173,7 @@ def create_responsive_render(
requirements={"description": "Text prompt describing the desired creative"},
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
# Concrete formats for backward compatibility
Expand All @@ -181,6 +199,7 @@ def create_responsive_render(
requirements={"description": "Text prompt describing the desired creative"},
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
CreativeFormat(
Expand All @@ -205,6 +224,7 @@ def create_responsive_render(
requirements={"description": "Text prompt describing the desired creative"},
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
CreativeFormat(
Expand All @@ -229,6 +249,7 @@ def create_responsive_render(
requirements={"description": "Text prompt describing the desired creative"},
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
CreativeFormat(
Expand All @@ -253,6 +274,7 @@ def create_responsive_render(
requirements={"description": "Text prompt describing the desired creative"},
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
CreativeFormat(
Expand All @@ -277,6 +299,7 @@ def create_responsive_render(
requirements={"description": "Text prompt describing the desired creative"},
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
CreativeFormat(
Expand All @@ -301,6 +324,7 @@ def create_responsive_render(
requirements={"description": "Text prompt describing the desired creative"},
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
CreativeFormat(
Expand All @@ -325,6 +349,7 @@ def create_responsive_render(
requirements={"description": "Text prompt describing the desired creative"},
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
]
Expand Down Expand Up @@ -386,6 +411,7 @@ def create_responsive_render(
asset_type=AssetType.vast,
required=True,
),
create_click_tracker_asset(),
],
),
# Concrete formats for backward compatibility
Expand Down Expand Up @@ -444,6 +470,7 @@ def create_responsive_render(
"description": "VAST 4.x compatible tag",
},
),
create_click_tracker_asset(),
],
),
CreativeFormat(
Expand Down Expand Up @@ -605,6 +632,7 @@ def create_responsive_render(
},
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
# Concrete formats for backward compatibility
Expand Down Expand Up @@ -636,6 +664,7 @@ def create_responsive_render(
},
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
CreativeFormat(
Expand Down Expand Up @@ -663,6 +692,7 @@ def create_responsive_render(
required=True,
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
CreativeFormat(
Expand Down Expand Up @@ -690,6 +720,7 @@ def create_responsive_render(
required=True,
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
CreativeFormat(
Expand Down Expand Up @@ -717,6 +748,7 @@ def create_responsive_render(
required=True,
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
CreativeFormat(
Expand Down Expand Up @@ -744,6 +776,7 @@ def create_responsive_render(
required=True,
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
CreativeFormat(
Expand Down Expand Up @@ -771,6 +804,7 @@ def create_responsive_render(
required=True,
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
CreativeFormat(
Expand Down Expand Up @@ -798,6 +832,7 @@ def create_responsive_render(
required=True,
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
]
Expand All @@ -823,6 +858,7 @@ def create_responsive_render(
},
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
# Concrete formats for backward compatibility
Expand All @@ -846,6 +882,7 @@ def create_responsive_render(
},
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
CreativeFormat(
Expand All @@ -867,6 +904,7 @@ def create_responsive_render(
},
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
CreativeFormat(
Expand All @@ -888,6 +926,7 @@ def create_responsive_render(
},
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
CreativeFormat(
Expand All @@ -909,6 +948,7 @@ def create_responsive_render(
},
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
CreativeFormat(
Expand All @@ -930,6 +970,7 @@ def create_responsive_render(
},
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
CreativeFormat(
Expand All @@ -951,6 +992,7 @@ def create_responsive_render(
},
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
]
Expand All @@ -973,6 +1015,7 @@ def create_responsive_render(
required=True,
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
]
Expand Down Expand Up @@ -1093,6 +1136,7 @@ def create_responsive_render(
},
),
create_impression_tracker_asset(),
create_click_tracker_asset(),
],
),
]
Expand Down
113 changes: 113 additions & 0 deletions tests/unit/test_click_tracker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
"""Unit tests for click_tracker asset functionality."""

from creative_agent.data.standard_formats import (
STANDARD_FORMATS,
create_click_tracker_asset,
)

# Expected formats that should have click_tracker
EXPECTED_FORMATS_WITH_CLICK_TRACKER = [
# Generative formats
"display_generative",
"display_300x250_generative",
"display_728x90_generative",
"display_320x50_generative",
"display_160x600_generative",
"display_336x280_generative",
"display_300x600_generative",
"display_970x250_generative",
# Image formats
"display_image",
"display_300x250_image",
"display_728x90_image",
"display_320x50_image",
"display_160x600_image",
"display_336x280_image",
"display_300x600_image",
"display_970x250_image",
# HTML formats
"display_html",
"display_300x250_html",
"display_728x90_html",
"display_160x600_html",
"display_336x280_html",
"display_300x600_html",
"display_970x250_html",
# JS format
"display_js",
# VAST formats
"video_vast",
"video_vast_30s",
# Native
"native_content",
]


class TestClickTrackerAsset:
"""Tests for the click_tracker asset helper function."""

def test_click_tracker_asset_id(self):
"""Test click_tracker has correct asset_id."""
tracker = create_click_tracker_asset()
assert tracker.asset_id == "click_tracker"

def test_click_tracker_asset_type(self):
"""Test click_tracker is a URL type asset."""
tracker = create_click_tracker_asset()
assert tracker.asset_type.value == "url"

def test_click_tracker_is_optional(self):
"""Test click_tracker is not required."""
tracker = create_click_tracker_asset()
assert tracker.required is False

def test_click_tracker_url_type(self):
"""Test click_tracker uses tracker_redirect url_type."""
tracker = create_click_tracker_asset()
assert tracker.requirements["url_type"] == "tracker_redirect"

def test_click_tracker_has_description(self):
"""Test click_tracker has a description in requirements."""
tracker = create_click_tracker_asset()
assert "description" in tracker.requirements
assert "click" in tracker.requirements["description"].lower()


class TestClickTrackerFormatCoverage:
"""Tests for click_tracker coverage across formats."""

def test_click_tracker_format_count(self):
"""Verify exactly 27 formats have click_tracker."""
count = sum(1 for fmt in STANDARD_FORMATS if "click_tracker" in [a.asset_id for a in (fmt.assets or [])])
assert count == 27

def test_click_tracker_on_expected_formats(self):
"""Verify click_tracker is on all expected formats."""
for fmt in STANDARD_FORMATS:
asset_ids = [a.asset_id for a in (fmt.assets or [])]
if fmt.format_id.id in EXPECTED_FORMATS_WITH_CLICK_TRACKER:
assert "click_tracker" in asset_ids, f"{fmt.format_id.id} should have click_tracker"

def test_click_tracker_only_on_expected_formats(self):
"""Verify click_tracker is only on formats in the expected list."""
for fmt in STANDARD_FORMATS:
asset_ids = [a.asset_id for a in (fmt.assets or [])]
if "click_tracker" in asset_ids:
assert fmt.format_id.id in EXPECTED_FORMATS_WITH_CLICK_TRACKER, (
f"{fmt.format_id.id} has click_tracker but is not in expected list"
)

def test_click_tracker_not_on_non_clickable_formats(self):
"""Verify click_tracker is NOT on formats without click functionality."""
# Audio and DOOH don't support click tracking
# VAST video formats DO support click tracking (external to VAST tag)
non_clickable_types = ["audio", "dooh"]
for fmt in STANDARD_FORMATS:
fmt_type = str(fmt.type).lower() if fmt.type else ""
asset_ids = [a.asset_id for a in (fmt.assets or [])]

# Skip if not a non-clickable type
if not any(t in fmt_type for t in non_clickable_types):
continue

assert "click_tracker" not in asset_ids, f"{fmt.format_id.id} ({fmt_type}) should not have click_tracker"