@@ -33,3 +33,86 @@ async def fake_fetch_all(query):
3333 assert "nullif(resources.b1g_publication_state_s, CAST(" in sql
3434 assert "nullif(resources.publication_state, CAST(" in sql
3535 assert ") = CAST(" in sql
36+
37+
38+ @pytest .mark .integration
39+ @pytest .mark .asyncio
40+ async def test_public_repo_counts_preserve_tag_and_publication_semantics (
41+ monkeypatch , db_connection
42+ ):
43+ """Exercise real PostgreSQL array/count behavior using connection-local tables."""
44+ import os
45+
46+ import asyncpg
47+ from sqlalchemy .schema import CreateTable
48+
49+ from db .models import ogm_harvest_runs , ogm_repos , ogm_resource_state
50+
51+ url = os .getenv ("OGM_QUERY_TEST_DATABASE_URL" )
52+ if not url and db_connection is not None :
53+ url = str (db_connection .url ).replace ("postgresql+asyncpg://" , "postgresql://" )
54+ if not url :
55+ pytest .skip ("Set OGM_QUERY_TEST_DATABASE_URL to run isolated PostgreSQL query tests" )
56+ connection = await asyncpg .connect (url )
57+ try :
58+ for table in (ogm_repos , ogm_harvest_runs , ogm_resource_state ):
59+ ddl = str (CreateTable (table ).compile (dialect = database ._backend ._dialect ))
60+ await connection .execute (ddl .replace ("CREATE TABLE" , "CREATE TEMP TABLE" , 1 ))
61+ await connection .execute ("""
62+ CREATE TEMP TABLE resources (
63+ id text PRIMARY KEY, "b1g_adminTags_sm" varchar[],
64+ b1g_publication_state_s varchar, publication_state varchar,
65+ gbl_suppressed_b boolean
66+ );
67+ INSERT INTO ogm_repos (ogm_repo_name, ogm_enabled) VALUES
68+ ('alpha', true), ('beta', false), ('empty', true);
69+ INSERT INTO ogm_resource_state
70+ (ogm_repo_name, ogm_resource_id, ogm_missing_since) VALUES
71+ ('alpha', 'active', NULL), ('alpha', 'missing', now()),
72+ ('beta', 'active', NULL);
73+ INSERT INTO ogm_harvest_runs
74+ (ogm_id, ogm_repo_name, ogm_trigger, ogm_status) VALUES
75+ (1, 'alpha', 'manual', 'failed'), (2, 'alpha', 'manual', 'success');
76+ """ )
77+ await connection .executemany (
78+ """INSERT INTO resources VALUES ($1, $2, $3, $4, $5)""" ,
79+ [
80+ ("duplicate" , ["ogm_repo:alpha" , "ogm_repo:alpha" ], None , None , None ),
81+ ("shared" , ["ogm_repo:alpha" , "ogm_repo:beta" ], "PUBLISHED" , "draft" , True ),
82+ ("fallback" , ["ogm_repo:alpha" ], "" , "Published" , False ),
83+ ("unpublished" , ["ogm_repo:alpha" ], "Draft" , "published" , True ),
84+ ("legacy" , ["ogm_repo:alpha" ], None , "draft" , None ),
85+ ("blank" , ["ogm_repo:beta" ], "" , "" , False ),
86+ ("null-tags" , None , None , None , True ),
87+ ("empty-tags" , [], None , None , True ),
88+ ("other-tags" , [None , "ogmXrepo:alpha" , "ogm_repo:unknown" ], None , None , True ),
89+ ],
90+ )
91+
92+ async def fetch_all (query ):
93+ compiled = query .compile (
94+ dialect = database ._backend ._dialect ,
95+ compile_kwargs = {"render_postcompile" : True },
96+ )
97+ parameters = [value for _ , value in sorted (compiled .params .items ())]
98+ return await connection .fetch (_compile_like_databases (query ), * parameters )
99+
100+ monkeypatch .setattr (ogm_repository .database , "fetch_all" , fetch_all )
101+ summaries = await OGMHarvestRepository ().list_public_repo_summaries ()
102+ assert [row ["ogm_repo_name" ] for row in summaries ] == ["alpha" , "beta" , "empty" ]
103+ counts = {
104+ row ["ogm_repo_name" ]: (
105+ row ["harvested_record_count" ],
106+ row ["available_record_count" ],
107+ row ["suppressed_record_count" ],
108+ row ["unpublished_record_count" ],
109+ )
110+ for row in summaries
111+ }
112+ assert counts == {"alpha" : (1 , 3 , 2 , 2 ), "beta" : (1 , 2 , 1 , 0 ), "empty" : (0 , 0 , 0 , 0 )}
113+ assert summaries [0 ]["last_run_id" ] == 2
114+ assert summaries [0 ]["last_crawl_status" ] == "success"
115+ assert summaries [1 ]["ogm_enabled" ] is False
116+ assert summaries [2 ]["last_run_id" ] is None
117+ finally :
118+ await connection .close ()
0 commit comments