1212from app .models .external_case_result import CaseStatus , ExternalCaseResult
1313from app .models .external_results import ExternalRunSession
1414from app .models .link import LinkSource , LinkType , RequirementTestCaseLink
15- from app .models .requirement import PriorityLevel , Requirement
15+ from app .models .requirement import PriorityLevel , Requirement , RequirementStatus , RequirementType
1616from app .models .test_case import TestCase , TestCaseStatus , TestCaseType
1717from app .schemas .external_results import CaseResultCreate , CaseResultUpdate
1818
@@ -21,6 +21,32 @@ def _outcome_value(outcome: Any) -> str:
2121 return getattr (outcome , "value" , str (outcome ))
2222
2323
24+ def _dedupe_requirement_ids (requirement_ids : list [UUID ]) -> list [UUID ]:
25+ seen_requirement_ids : set [UUID ] = set ()
26+ deduped_requirement_ids : list [UUID ] = []
27+
28+ for requirement_id in requirement_ids :
29+ if requirement_id in seen_requirement_ids :
30+ continue
31+ seen_requirement_ids .add (requirement_id )
32+ deduped_requirement_ids .append (requirement_id )
33+
34+ return deduped_requirement_ids
35+
36+
37+ def _dedupe_requirement_external_ids (requirement_external_ids : list [str ]) -> list [str ]:
38+ seen_external_ids : set [str ] = set ()
39+ deduped_external_ids : list [str ] = []
40+
41+ for external_id in requirement_external_ids :
42+ if external_id in seen_external_ids :
43+ continue
44+ seen_external_ids .add (external_id )
45+ deduped_external_ids .append (external_id )
46+
47+ return deduped_external_ids
48+
49+
2450async def _get_requirement_ids_for_test_case (
2551 db : AsyncSession ,
2652 * ,
@@ -90,7 +116,7 @@ async def _link_requirements(
90116 if not requirement_ids :
91117 return [], []
92118
93- deduped_ids = list ( dict . fromkeys ( requirement_ids ) )
119+ deduped_ids = _dedupe_requirement_ids ( requirement_ids )
94120 resolvable_ids , unresolved_ids = await _resolve_requirement_ids (db , requirement_ids = deduped_ids )
95121
96122 values = [
@@ -113,12 +139,7 @@ async def _link_requirements(
113139 stmt = insert_stmt .on_conflict_do_nothing (index_elements = ["requirement_id" , "test_case_id" ])
114140 await db .execute (stmt )
115141
116- linked_rows = await db .execute (
117- select (RequirementTestCaseLink .requirement_id )
118- .where (RequirementTestCaseLink .test_case_id == test_case_id )
119- .where (RequirementTestCaseLink .requirement_id .in_ (deduped_ids ))
120- )
121- return list (linked_rows .scalars ().all ()), unresolved_ids
142+ return await _get_requirement_ids_for_test_case (db , test_case_id = test_case_id ), unresolved_ids
122143
123144
124145async def _resolve_requirement_ids (
@@ -138,6 +159,52 @@ async def _resolve_requirement_ids(
138159 return resolvable_ids , unresolved_ids
139160
140161
162+ async def _resolve_requirement_external_ids (
163+ db : AsyncSession ,
164+ * ,
165+ requirement_external_ids : list [str ] | None ,
166+ auto_register_requirements : bool ,
167+ ) -> tuple [list [UUID ], list [str ]]:
168+ if not requirement_external_ids :
169+ return [], []
170+
171+ deduped_external_ids = _dedupe_requirement_external_ids (requirement_external_ids )
172+ requirement_rows = await db .execute (select (Requirement ).where (Requirement .external_id .in_ (deduped_external_ids )))
173+ requirements_by_external_id : dict [str , Requirement ] = {}
174+ for requirement in requirement_rows .scalars ().all ():
175+ external_id = requirement .external_id
176+ if isinstance (external_id , str ):
177+ requirements_by_external_id [external_id ] = requirement
178+
179+ resolved_ids : list [UUID ] = []
180+ unresolved_ids : list [str ] = []
181+
182+ for submitted_external_id in deduped_external_ids :
183+ requirement = requirements_by_external_id .get (submitted_external_id )
184+ if requirement is not None :
185+ resolved_ids .append (requirement .id )
186+ continue
187+
188+ if not auto_register_requirements :
189+ unresolved_ids .append (submitted_external_id )
190+ continue
191+
192+ requirement = Requirement (
193+ external_id = submitted_external_id ,
194+ title = submitted_external_id ,
195+ description = f"Auto-registered from external ID { submitted_external_id } " ,
196+ type = RequirementType .FUNCTIONAL ,
197+ priority = PriorityLevel .MEDIUM ,
198+ status = RequirementStatus .DRAFT ,
199+ )
200+ db .add (requirement )
201+ await db .flush ()
202+ requirements_by_external_id [submitted_external_id ] = requirement
203+ resolved_ids .append (requirement .id )
204+
205+ return resolved_ids , unresolved_ids
206+
207+
141208async def create_case_result (
142209 db : AsyncSession ,
143210 * ,
@@ -158,7 +225,13 @@ async def create_case_result(
158225 db ,
159226 requirement_ids = payload .requirement_ids ,
160227 )
228+ _resolved_external_ids , unresolved_external_ids = await _resolve_requirement_external_ids (
229+ db ,
230+ requirement_external_ids = payload .requirement_external_ids ,
231+ auto_register_requirements = False ,
232+ )
161233 existing .unresolved_requirement_ids = unresolved_ids
234+ existing .unresolved_requirement_external_ids = unresolved_external_ids
162235 return existing , False
163236
164237 session_result = await db .execute (select (ExternalRunSession ).where (ExternalRunSession .id == session_id ))
@@ -190,15 +263,25 @@ async def create_case_result(
190263 )
191264 db .add (case_result )
192265 await db .flush ()
193- linked_ids , unresolved_ids = await _link_requirements (
266+ resolvable_ids , unresolved_ids = await _resolve_requirement_ids (
194267 db ,
195- test_case_id = test_case .id ,
196268 requirement_ids = payload .requirement_ids ,
197269 )
270+ resolved_external_ids , unresolved_external_ids = await _resolve_requirement_external_ids (
271+ db ,
272+ requirement_external_ids = payload .requirement_external_ids ,
273+ auto_register_requirements = payload .auto_register_requirements ,
274+ )
275+ linked_ids , _ = await _link_requirements (
276+ db ,
277+ test_case_id = test_case .id ,
278+ requirement_ids = _dedupe_requirement_ids (resolvable_ids + resolved_external_ids ),
279+ )
198280 await db .commit ()
199281 await db .refresh (case_result )
200282 case_result .requirement_ids = linked_ids
201283 case_result .unresolved_requirement_ids = unresolved_ids
284+ case_result .unresolved_requirement_external_ids = unresolved_external_ids
202285 return case_result , True
203286
204287
0 commit comments