Skip to content

Commit 9585422

Browse files
committed
test: Add synchronization tests for GCP DNS zones and record sets
1 parent 6e3e090 commit 9585422

File tree

2 files changed

+229
-0
lines changed

2 files changed

+229
-0
lines changed

tests/integration/cartography/intel/gcp/test_crm.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,132 @@ def test_load_gcp_projects_without_parent(neo4j_session):
8484
)
8585
actual_nodes = {(n["d.id"]) for n in nodes}
8686
assert actual_nodes == expected_nodes
87+
88+
89+
def test_sync_gcp_organizations(neo4j_session):
90+
"""
91+
Test that sync_gcp_organizations correctly syncs GCP organizations to Neo4j.
92+
"""
93+
common_job_parameters = {"UPDATE_TAG": TEST_UPDATE_TAG}
94+
95+
# Load organizations
96+
cartography.intel.gcp.crm.load_gcp_organizations(
97+
neo4j_session,
98+
tests.data.gcp.crm.GCP_ORGANIZATIONS,
99+
TEST_UPDATE_TAG,
100+
)
101+
102+
# Verify organizations are loaded correctly
103+
expected_nodes = {"organizations/1337"}
104+
nodes = neo4j_session.run("MATCH (o:GCPOrganization) RETURN o.id")
105+
actual_nodes = {n["o.id"] for n in nodes}
106+
assert actual_nodes == expected_nodes
107+
108+
# Verify organization properties
109+
org_query = """
110+
MATCH (o:GCPOrganization{id: 'organizations/1337'})
111+
RETURN o.displayname, o.lifecyclestate, o.lastupdated
112+
"""
113+
result = neo4j_session.run(org_query).single()
114+
assert result["o.displayname"] == "example.com"
115+
assert result["o.lifecyclestate"] == "ACTIVE"
116+
assert result["o.lastupdated"] == TEST_UPDATE_TAG
117+
118+
119+
def test_sync_gcp_folders(neo4j_session):
120+
"""
121+
Test that sync_gcp_folders correctly syncs GCP folders to Neo4j.
122+
"""
123+
# Load parent organization
124+
cartography.intel.gcp.crm.load_gcp_organizations(
125+
neo4j_session,
126+
tests.data.gcp.crm.GCP_ORGANIZATIONS,
127+
TEST_UPDATE_TAG,
128+
)
129+
130+
common_job_parameters = {"UPDATE_TAG": TEST_UPDATE_TAG}
131+
132+
# Load folders
133+
cartography.intel.gcp.crm.load_gcp_folders(
134+
neo4j_session,
135+
tests.data.gcp.crm.GCP_FOLDERS,
136+
TEST_UPDATE_TAG,
137+
)
138+
139+
# Verify folders are loaded correctly
140+
expected_nodes = {"folders/1414"}
141+
nodes = neo4j_session.run("MATCH (f:GCPFolder) RETURN f.id")
142+
actual_nodes = {n["f.id"] for n in nodes}
143+
assert actual_nodes == expected_nodes
144+
145+
# Verify folder properties
146+
folder_query = """
147+
MATCH (f:GCPFolder{id: 'folders/1414'})
148+
RETURN f.displayname, f.lifecyclestate, f.lastupdated
149+
"""
150+
result = neo4j_session.run(folder_query).single()
151+
assert result["f.displayname"] == "my-folder"
152+
assert result["f.lifecyclestate"] == "ACTIVE"
153+
assert result["f.lastupdated"] == TEST_UPDATE_TAG
154+
155+
# Verify relationship: Organization -> Folder
156+
relationship_query = """
157+
MATCH (o:GCPOrganization{id: 'organizations/1337'})-[:RESOURCE]->(f:GCPFolder{id: 'folders/1414'})
158+
RETURN COUNT(*) as count
159+
"""
160+
result = neo4j_session.run(relationship_query).single()
161+
assert result["count"] == 1
162+
163+
164+
def test_sync_gcp_projects(neo4j_session):
165+
"""
166+
Test that sync_gcp_projects correctly syncs GCP projects to Neo4j.
167+
"""
168+
# Clear existing project nodes
169+
neo4j_session.run("MATCH (p:GCPProject) DETACH DELETE p")
170+
171+
# Load referenced parent organization and folder
172+
cartography.intel.gcp.crm.load_gcp_organizations(
173+
neo4j_session,
174+
tests.data.gcp.crm.GCP_ORGANIZATIONS,
175+
TEST_UPDATE_TAG,
176+
)
177+
cartography.intel.gcp.crm.load_gcp_folders(
178+
neo4j_session,
179+
tests.data.gcp.crm.GCP_FOLDERS,
180+
TEST_UPDATE_TAG,
181+
)
182+
183+
common_job_parameters = {"UPDATE_TAG": TEST_UPDATE_TAG}
184+
185+
# Run sync
186+
cartography.intel.gcp.crm.sync_gcp_projects(
187+
neo4j_session,
188+
tests.data.gcp.crm.GCP_PROJECTS,
189+
TEST_UPDATE_TAG,
190+
common_job_parameters,
191+
)
192+
193+
expected_nodes = {"this-project-has-a-parent-232323"}
194+
nodes = neo4j_session.run("MATCH (p:GCPProject) RETURN p.id")
195+
actual_nodes = {n["p.id"] for n in nodes}
196+
assert actual_nodes == expected_nodes
197+
198+
# project properties
199+
project_query = """
200+
MATCH (p:GCPProject{id: 'this-project-has-a-parent-232323'})
201+
RETURN p.projectnumber, p.displayname, p.lifecyclestate, p.lastupdated
202+
"""
203+
result = neo4j_session.run(project_query).single()
204+
assert result["p.projectnumber"] == "232323"
205+
assert result["p.displayname"] == "Group 1"
206+
assert result["p.lifecyclestate"] == "ACTIVE"
207+
assert result["p.lastupdated"] == TEST_UPDATE_TAG
208+
209+
# Organization -> Folder -> Project
210+
hierarchy_query = """
211+
MATCH (o:GCPOrganization{id: 'organizations/1337'})-[:RESOURCE]->(f:GCPFolder{id: 'folders/1414'})-[:RESOURCE]->(p:GCPProject{id: 'this-project-has-a-parent-232323'})
212+
RETURN COUNT(*) as count
213+
"""
214+
result = neo4j_session.run(hierarchy_query).single()
215+
assert result["count"] == 1

tests/integration/cartography/intel/gcp/test_dns.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,103 @@ def test_rrs_relationships(neo4j_session):
131131
actual = {(r["n1.id"], r["n2.id"]) for r in result}
132132

133133
assert actual == expected
134+
135+
def test_sync(neo4j_session):
136+
"""
137+
Test that DNS zones and record sets sync correctly into Neo4j.
138+
"""
139+
140+
common_job_parameters = {
141+
"UPDATE_TAG": TEST_UPDATE_TAG,
142+
}
143+
144+
145+
dns_zones_data = tests.data.gcp.dns.DNS_ZONES
146+
dns_rrs_data = tests.data.gcp.dns.DNS_RRS
147+
148+
# Create test GCPProject
149+
neo4j_session.run(
150+
"""
151+
MERGE (gcp:GCPProject {id: $PROJECT_NUMBER})
152+
ON CREATE SET gcp.firstseen = timestamp()
153+
SET gcp.lastupdated = $UPDATE_TAG
154+
""",
155+
PROJECT_NUMBER=TEST_PROJECT_NUMBER,
156+
UPDATE_TAG=TEST_UPDATE_TAG,
157+
)
158+
159+
# Simulate sync's
160+
cartography.intel.gcp.dns.load_dns_zones(
161+
neo4j_session,
162+
dns_zones_data,
163+
TEST_PROJECT_NUMBER,
164+
TEST_UPDATE_TAG,
165+
)
166+
cartography.intel.gcp.dns.load_rrs(
167+
neo4j_session,
168+
dns_rrs_data,
169+
TEST_PROJECT_NUMBER,
170+
TEST_UPDATE_TAG,
171+
)
172+
173+
# Simulate cleanup
174+
cartography.intel.gcp.dns.cleanup_dns_records(
175+
neo4j_session,
176+
common_job_parameters,
177+
)
178+
179+
# Verify DNS Zones
180+
expected_zone_nodes = {
181+
"111111111111111111111",
182+
"2222222222222222222",
183+
}
184+
zone_nodes = neo4j_session.run(
185+
"MATCH (r:GCPDNSZone) RETURN r.id",
186+
)
187+
actual_zone_nodes = {n["r.id"] for n in zone_nodes}
188+
assert actual_zone_nodes == expected_zone_nodes
189+
190+
# Verify DNS Zone relationships to GCPProject
191+
expected_zone_relationships = {
192+
(TEST_PROJECT_NUMBER, "111111111111111111111"),
193+
(TEST_PROJECT_NUMBER, "2222222222222222222"),
194+
}
195+
zone_relationships = neo4j_session.run(
196+
"""
197+
MATCH (n1:GCPProject)-[:RESOURCE]->(n2:GCPDNSZone)
198+
RETURN n1.id, n2.id
199+
""",
200+
)
201+
actual_zone_relationships = {
202+
(r["n1.id"], r["n2.id"]) for r in zone_relationships
203+
}
204+
assert actual_zone_relationships == expected_zone_relationships
205+
206+
# Verify Resource Record Sets
207+
expected_rrs_nodes = {
208+
"a.zone-1.example.com.",
209+
"b.zone-1.example.com.",
210+
"a.zone-2.example.com.",
211+
}
212+
rrs_nodes = neo4j_session.run(
213+
"MATCH (r:GCPRecordSet) RETURN r.id",
214+
)
215+
actual_rrs_nodes = {n["r.id"] for n in rrs_nodes}
216+
assert actual_rrs_nodes == expected_rrs_nodes
217+
218+
# Verify Resource Record Set relationships to DNS Zones
219+
expected_rrs_relationships = {
220+
("111111111111111111111", "a.zone-1.example.com."),
221+
("111111111111111111111", "b.zone-1.example.com."),
222+
("2222222222222222222", "a.zone-2.example.com."),
223+
}
224+
rrs_relationships = neo4j_session.run(
225+
"""
226+
MATCH (n1:GCPDNSZone)-[:HAS_RECORD]->(n2:GCPRecordSet)
227+
RETURN n1.id, n2.id
228+
""",
229+
)
230+
actual_rrs_relationships = {
231+
(r["n1.id"], r["n2.id"]) for r in rrs_relationships
232+
}
233+
assert actual_rrs_relationships == expected_rrs_relationships

0 commit comments

Comments
 (0)