Skip to content

Commit f348ee7

Browse files
authored
[SYNPY-1295] Trim down integration tests (#1199)
* Use github copilot agent mode to trim down the number of required tests and combine similar logic and update retry parameters and refactor async test setup methods for consistency
1 parent fdd3d74 commit f348ee7

24 files changed

+5554
-11963
lines changed

.github/workflows/build.yml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ jobs:
8383
path: |
8484
${{ steps.get-dependencies.outputs.site_packages_loc }}
8585
${{ steps.get-dependencies.outputs.site_bin_dir }}
86-
key: ${{ runner.os }}-${{ matrix.python }}-build-${{ env.cache-name }}-${{ hashFiles('setup.py') }}-v22
86+
key: ${{ runner.os }}-${{ matrix.python }}-build-${{ env.cache-name }}-${{ hashFiles('setup.py') }}-v23
8787

8888
- name: Install py-dependencies
8989
if: steps.cache-dependencies.outputs.cache-hit != 'true'
@@ -199,8 +199,21 @@ jobs:
199199
export SYNAPSE_OTEL_INTEGRATION_TEST_EXPORTER="file"
200200
fi
201201
202+
# Setup ignore patterns based on Python version
203+
IGNORE_FLAGS="--ignore=tests/integration/synapseclient/test_command_line_client.py"
204+
205+
if [ "${{ matrix.python }}" == "3.9" ]; then
206+
# For min Python version, ignore async tests
207+
IGNORE_FLAGS="$IGNORE_FLAGS --ignore=tests/integration/synapseclient/models/async/"
208+
echo "Running integration tests for Min Python version (3.9) - ignoring async tests"
209+
elif [ "${{ matrix.python }}" == "3.13" ]; then
210+
# For max Python version, ignore synchronous tests
211+
IGNORE_FLAGS="$IGNORE_FLAGS --ignore=tests/integration/synapseclient/models/synchronous/"
212+
echo "Running integration tests for Max Python version (3.13) - ignoring synchronous tests"
213+
fi
214+
202215
# use loadscope to avoid issues running tests concurrently that share scoped fixtures
203-
pytest -sv --reruns 3 --cov-append --cov=. --cov-report xml tests/integration -n 8 --ignore=tests/integration/synapseclient/test_command_line_client.py --dist loadscope
216+
pytest -sv --reruns 3 --cov-append --cov=. --cov-report xml tests/integration -n 8 $IGNORE_FLAGS --dist loadscope
204217
205218
# Execute the CLI tests in a non-dist way because they were causing some test instability when being run concurrently
206219
pytest -sv --reruns 3 --cov-append --cov=. --cov-report xml tests/integration/synapseclient/test_command_line_client.py

synapseclient/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,7 @@ def setEndpoints(
769769
timeout=self._http_timeout_seconds,
770770
),
771771
verbose=self.debug,
772-
**STANDARD_RETRY_PARAMS,
772+
**{**STANDARD_RETRY_PARAMS, "retries": 2},
773773
)
774774
if response.status_code == 301:
775775
endpoints[point] = response.headers["location"]

tests/integration/synapseclient/models/async/test_activity_async.py

Lines changed: 115 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,60 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None:
2121
self.syn = syn
2222
self.schedule_for_cleanup = schedule_for_cleanup
2323

24-
async def test_store_with_parent_and_id(self, project: Synapse_Project) -> None:
25-
# GIVEN a file in a project
24+
async def create_file_with_activity(
25+
self,
26+
project: Synapse_Project,
27+
activity: Activity = None,
28+
store_file: bool = True,
29+
) -> File:
30+
"""Helper to create a file with optional activity"""
2631
path = utils.make_bogus_uuid_file()
2732
file = File(
28-
parent_id=project["id"], path=path, name=f"bogus_file_{str(uuid.uuid4())}"
33+
parent_id=project["id"],
34+
path=path,
35+
name=f"bogus_file_{str(uuid.uuid4())}",
36+
activity=activity,
2937
)
3038
self.schedule_for_cleanup(file.path)
31-
await file.store_async()
32-
self.schedule_for_cleanup(file.id)
3339

34-
# AND an activity I want to store
40+
if store_file:
41+
await file.store_async()
42+
self.schedule_for_cleanup(file.id)
43+
44+
return file
45+
46+
async def verify_activity_properties(
47+
self, activity, expected_name, expected_description, has_references=False
48+
):
49+
"""Helper to verify common activity properties"""
50+
assert activity.name == expected_name
51+
assert activity.description == expected_description
52+
assert activity.id is not None
53+
assert activity.etag is not None
54+
assert activity.created_on is not None
55+
assert activity.modified_on is not None
56+
assert activity.created_by is not None
57+
assert activity.modified_by is not None
58+
59+
if has_references:
60+
assert activity.used[0].url == BOGUS_URL
61+
assert activity.used[0].name == "example"
62+
assert activity.used[1].target_id == "syn456"
63+
assert activity.used[1].target_version_number == 1
64+
assert activity.executed[0].url == BOGUS_URL
65+
assert activity.executed[0].name == "example"
66+
assert activity.executed[1].target_id == "syn789"
67+
assert activity.executed[1].target_version_number == 1
68+
else:
69+
assert activity.used == []
70+
assert activity.executed == []
71+
72+
async def test_activity_lifecycle(self, project: Synapse_Project) -> None:
73+
"""Test complete activity lifecycle - create, update, retrieve, and delete"""
74+
# GIVEN a file in a project
75+
file = await self.create_file_with_activity(project)
76+
77+
# AND an activity with references
3578
activity = Activity(
3679
name="some_name",
3780
description="some_description",
@@ -49,94 +92,77 @@ async def test_store_with_parent_and_id(self, project: Synapse_Project) -> None:
4992
result = await activity.store_async(parent=file)
5093
self.schedule_for_cleanup(result.id)
5194

52-
# THEN I expect the activity to be stored
95+
# THEN I expect the activity to be stored correctly
5396
assert result == activity
54-
assert result.id is not None
55-
assert result.etag is not None
56-
assert result.created_on is not None
57-
assert result.modified_on is not None
58-
assert result.created_by is not None
59-
assert result.modified_by is not None
60-
assert result.used[0].url == BOGUS_URL
61-
assert result.used[0].name == "example"
62-
assert result.used[1].target_id == "syn456"
63-
assert result.used[1].target_version_number == 1
64-
assert result.executed[0].url == BOGUS_URL
65-
assert result.executed[0].name == "example"
66-
assert result.executed[1].target_id == "syn789"
67-
assert result.executed[1].target_version_number == 1
68-
69-
# GIVEN our already stored activity
70-
modified_activity = activity
71-
72-
# WHEN I modify the activity
73-
modified_activity.name = "modified_name"
74-
modified_activity.description = "modified_description"
75-
76-
# AND I store the modified activity without a parent
77-
modified_result = await modified_activity.store_async()
97+
await self.verify_activity_properties(
98+
result, "some_name", "some_description", has_references=True
99+
)
100+
101+
# WHEN I modify and store the activity
102+
result.name = "modified_name"
103+
result.description = "modified_description"
104+
modified_result = await result.store_async()
78105

79106
# THEN I expect the modified activity to be stored
80-
assert modified_result == modified_activity
81-
assert modified_result.id is not None
82-
assert modified_result.etag is not None
83-
assert modified_result.created_on is not None
84-
assert modified_result.modified_on is not None
85-
assert modified_result.created_by is not None
86-
assert modified_result.modified_by is not None
87-
assert modified_result.name == "modified_name"
88-
assert modified_result.description == "modified_description"
89-
assert modified_result.used[0].url == BOGUS_URL
90-
assert modified_result.used[0].name == "example"
91-
assert modified_result.used[1].target_id == "syn456"
92-
assert modified_result.used[1].target_version_number == 1
93-
assert modified_result.executed[0].url == BOGUS_URL
94-
assert modified_result.executed[0].name == "example"
95-
assert modified_result.executed[1].target_id == "syn789"
96-
assert modified_result.executed[1].target_version_number == 1
107+
await self.verify_activity_properties(
108+
modified_result,
109+
"modified_name",
110+
"modified_description",
111+
has_references=True,
112+
)
97113

98-
# Clean up
114+
# WHEN I get the activity from the file
115+
retrieved_activity = await Activity.from_parent_async(parent=file)
116+
117+
# THEN I expect the retrieved activity to match the modified one
118+
assert retrieved_activity.name == "modified_name"
119+
assert retrieved_activity.description == "modified_description"
120+
await self.verify_activity_properties(
121+
retrieved_activity,
122+
"modified_name",
123+
"modified_description",
124+
has_references=True,
125+
)
126+
127+
# WHEN I delete the activity
99128
await result.delete_async(parent=file)
100129

101-
async def test_store_with_no_references(self, project: Synapse_Project) -> None:
102-
# GIVEN a file in a project that has an activity with no references
130+
# THEN I expect no activity to be associated with the file
131+
activity_after_delete = await Activity.from_parent_async(parent=file)
132+
assert activity_after_delete is None
133+
134+
async def test_store_activity_with_no_references(
135+
self, project: Synapse_Project
136+
) -> None:
137+
"""Test storing an activity without references"""
138+
# GIVEN an activity with no references
103139
activity = Activity(
104-
name="some_name",
105-
description="some_description",
140+
name="simple_activity",
141+
description="activity with no references",
106142
)
107-
path = utils.make_bogus_uuid_file()
108-
file = File(
109-
parent_id=project["id"],
110-
path=path,
111-
name=f"bogus_file_{str(uuid.uuid4())}",
112-
activity=activity,
113-
)
114-
self.schedule_for_cleanup(file.path)
115143

116-
# WHEN I store the file with the activity
117-
await file.store_async()
118-
self.schedule_for_cleanup(file.id)
119-
120-
# THEN I expect the activity to have been stored
121-
assert file.activity.name == activity.name
122-
assert file.activity.description == activity.description
123-
assert file.activity.used == []
124-
assert file.activity.executed == []
125-
assert file.activity.id is not None
126-
assert file.activity.etag is not None
127-
assert file.activity.created_on is not None
128-
assert file.activity.modified_on is not None
129-
assert file.activity.created_by is not None
130-
assert file.activity.modified_by is not None
144+
# AND a file with that activity
145+
file = await self.create_file_with_activity(project, activity=activity)
146+
147+
# THEN I expect the activity to have been stored properly
148+
await self.verify_activity_properties(
149+
file.activity,
150+
"simple_activity",
151+
"activity with no references",
152+
has_references=False,
153+
)
131154

132155
# Clean up
133156
await file.activity.delete_async(parent=file)
134157

135-
async def test_from_parent(self, project: Synapse_Project) -> None:
136-
# GIVEN a file in a project that has an activity
158+
async def test_store_activity_via_file_creation(
159+
self, project: Synapse_Project
160+
) -> None:
161+
"""Test storing an activity as part of file creation"""
162+
# GIVEN an activity with references
137163
activity = Activity(
138-
name="some_name",
139-
description="some_description",
164+
name="file_activity",
165+
description="activity stored with file",
140166
used=[
141167
UsedURL(name="example", url=BOGUS_URL),
142168
UsedEntity(target_id="syn456", target_version_number=1),
@@ -146,67 +172,17 @@ async def test_from_parent(self, project: Synapse_Project) -> None:
146172
UsedEntity(target_id="syn789", target_version_number=1),
147173
],
148174
)
149-
path = utils.make_bogus_uuid_file()
150-
file = File(
151-
parent_id=project["id"],
152-
path=path,
153-
name=f"bogus_file_{str(uuid.uuid4())}",
154-
activity=activity,
155-
)
156-
self.schedule_for_cleanup(file.path)
157-
await file.store_async()
158-
self.schedule_for_cleanup(file.id)
159175

160-
# WHEN I get the activity from the file
161-
result = await Activity.from_parent_async(parent=file)
162-
163-
# THEN I expect the activity to be returned
164-
assert result == activity
165-
assert result.name == "some_name"
166-
assert result.description == "some_description"
167-
assert result.id is not None
168-
assert result.etag is not None
169-
assert result.created_on is not None
170-
assert result.modified_on is not None
171-
assert result.created_by is not None
172-
assert result.modified_by is not None
173-
assert result.used[0].url == BOGUS_URL
174-
assert result.used[0].name == "example"
175-
assert result.used[1].target_id == "syn456"
176-
assert result.used[1].target_version_number == 1
177-
assert result.executed[0].url == BOGUS_URL
178-
assert result.executed[0].name == "example"
179-
assert result.executed[1].target_id == "syn789"
180-
assert result.executed[1].target_version_number == 1
176+
# WHEN I create a file with the activity
177+
file = await self.create_file_with_activity(project, activity=activity)
181178

182-
# Clean up
183-
await result.delete_async(parent=file)
184-
185-
async def test_delete(self, project: Synapse_Project) -> None:
186-
# GIVEN a file in a project that has an activity
187-
activity = Activity(
188-
name="some_name",
189-
description="some_description",
179+
# THEN I expect the activity to have been stored with the file
180+
await self.verify_activity_properties(
181+
file.activity,
182+
"file_activity",
183+
"activity stored with file",
184+
has_references=True,
190185
)
191-
path = utils.make_bogus_uuid_file()
192-
file = File(
193-
parent_id=project["id"],
194-
path=path,
195-
name=f"bogus_file_{str(uuid.uuid4())}",
196-
activity=activity,
197-
)
198-
self.schedule_for_cleanup(file.path)
199-
200-
# AND I store the file with the activity
201-
await file.store_async()
202-
self.schedule_for_cleanup(file.id)
203-
204-
# AND the activity exists
205-
assert file.activity.id is not None
206186

207-
# WHEN I delete the activity
187+
# Clean up
208188
await file.activity.delete_async(parent=file)
209-
210-
# THEN I expect to receieve None
211-
activity = await Activity.from_parent_async(parent=file)
212-
assert activity is None

0 commit comments

Comments
 (0)