mirror of
https://github.com/dgtlmoon/changedetection.io.git
synced 2026-04-30 23:00:30 +00:00
e9e8c8d218
Build and push containers / metadata (push) Has been cancelled
Build and push containers / build-push-containers (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/amd64 (alpine) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/arm64 (alpine) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/amd64 (main) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/arm/v7 (main) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/arm/v8 (main) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/arm64 (main) (push) Has been cancelled
ChangeDetection.io App Test / lint-code (push) Has been cancelled
ChangeDetection.io App Test / lint-translations (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built package works basically. (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-10 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-11 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-12 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-13 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-14 (push) Has been cancelled
138 lines
4.5 KiB
Python
138 lines
4.5 KiB
Python
"""
|
|
Unit tests for changedetectionio/llm/prompt_builder.py
|
|
|
|
All functions are pure — no external dependencies needed.
|
|
"""
|
|
import pytest
|
|
from changedetectionio.llm.prompt_builder import (
|
|
build_eval_prompt,
|
|
build_eval_system_prompt,
|
|
build_setup_prompt,
|
|
build_setup_system_prompt,
|
|
SNAPSHOT_CONTEXT_CHARS,
|
|
)
|
|
|
|
|
|
class TestBuildEvalPrompt:
|
|
def test_contains_intent(self):
|
|
prompt = build_eval_prompt(intent='Alert on price drops', diff='- $500\n+ $400')
|
|
assert 'Alert on price drops' in prompt
|
|
|
|
def test_contains_diff(self):
|
|
prompt = build_eval_prompt(intent='price', diff='- $500\n+ $400')
|
|
assert '- $500' in prompt
|
|
assert '+ $400' in prompt
|
|
|
|
def test_optional_url_included_when_provided(self):
|
|
prompt = build_eval_prompt(
|
|
intent='price',
|
|
diff='some diff',
|
|
url='https://example.com/product',
|
|
)
|
|
assert 'https://example.com/product' in prompt
|
|
|
|
def test_url_absent_when_not_provided(self):
|
|
prompt = build_eval_prompt(intent='price', diff='diff')
|
|
assert 'URL:' not in prompt
|
|
|
|
def test_optional_title_included_when_provided(self):
|
|
prompt = build_eval_prompt(
|
|
intent='price',
|
|
diff='diff',
|
|
title='Example Product Page',
|
|
)
|
|
assert 'Example Product Page' in prompt
|
|
|
|
def test_snapshot_context_included(self):
|
|
snapshot = 'Current price: $400. Stock: in stock. Description: widget.'
|
|
prompt = build_eval_prompt(
|
|
intent='price',
|
|
diff='- $500\n+ $400',
|
|
current_snapshot=snapshot,
|
|
)
|
|
# Snapshot excerpt should appear somewhere in the prompt
|
|
assert 'Current price' in prompt or '$400' in prompt
|
|
|
|
def test_large_snapshot_trimmed_to_budget(self):
|
|
# Snapshot larger than SNAPSHOT_CONTEXT_CHARS should be trimmed
|
|
large_snapshot = 'irrelevant content line\n' * 2000
|
|
prompt = build_eval_prompt(
|
|
intent='price drop',
|
|
diff='changed',
|
|
current_snapshot=large_snapshot,
|
|
)
|
|
# Prompt should not be astronomically large
|
|
assert len(prompt) < len(large_snapshot)
|
|
|
|
def test_empty_snapshot_skipped(self):
|
|
prompt_with = build_eval_prompt(intent='x', diff='d', current_snapshot='some text')
|
|
prompt_without = build_eval_prompt(intent='x', diff='d', current_snapshot='')
|
|
# Without snapshot should be shorter
|
|
assert len(prompt_without) < len(prompt_with)
|
|
|
|
|
|
class TestBuildEvalSystemPrompt:
|
|
def test_returns_string(self):
|
|
result = build_eval_system_prompt()
|
|
assert isinstance(result, str)
|
|
assert len(result) > 0
|
|
|
|
def test_instructs_json_only_output(self):
|
|
result = build_eval_system_prompt()
|
|
assert 'JSON' in result or 'json' in result.lower()
|
|
|
|
def test_defines_important_field(self):
|
|
result = build_eval_system_prompt()
|
|
assert 'important' in result
|
|
|
|
def test_defines_summary_field(self):
|
|
result = build_eval_system_prompt()
|
|
assert 'summary' in result
|
|
|
|
|
|
class TestBuildSetupPrompt:
|
|
def test_contains_intent(self):
|
|
prompt = build_setup_prompt(
|
|
intent='monitor footer changes',
|
|
snapshot_text='<footer>Copyright 2024</footer>',
|
|
)
|
|
assert 'monitor footer changes' in prompt
|
|
|
|
def test_contains_url_when_provided(self):
|
|
prompt = build_setup_prompt(
|
|
intent='price',
|
|
snapshot_text='price: $10',
|
|
url='https://shop.example.com',
|
|
)
|
|
assert 'https://shop.example.com' in prompt
|
|
|
|
def test_url_absent_when_not_provided(self):
|
|
prompt = build_setup_prompt(intent='price', snapshot_text='text')
|
|
assert 'URL:' not in prompt
|
|
|
|
def test_large_snapshot_trimmed(self):
|
|
big_snapshot = 'unrelated junk line\n' * 500
|
|
prompt = build_setup_prompt(
|
|
intent='monitor price section',
|
|
snapshot_text=big_snapshot,
|
|
)
|
|
assert len(prompt) < len(big_snapshot)
|
|
|
|
|
|
class TestBuildSetupSystemPrompt:
|
|
def test_returns_string(self):
|
|
result = build_setup_system_prompt()
|
|
assert isinstance(result, str)
|
|
|
|
def test_forbids_positional_selectors(self):
|
|
result = build_setup_system_prompt()
|
|
assert 'nth-child' in result or 'positional' in result
|
|
|
|
def test_defines_needs_prefilter_field(self):
|
|
result = build_setup_system_prompt()
|
|
assert 'needs_prefilter' in result
|
|
|
|
def test_defines_selector_field(self):
|
|
result = build_setup_system_prompt()
|
|
assert 'selector' in result
|