Skip to content

Commit 33b778a

Browse files
committed
[sublime] add tests
1 parent e195f7c commit 33b778a

7 files changed

Lines changed: 780 additions & 0 deletions

File tree

external-import/sublime/tests/__init__.py

Whitespace-only changes.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""
2+
Pytest configuration for Sublime Security connector tests.
3+
"""
4+
5+
import sys
6+
from pathlib import Path
7+
from unittest.mock import Mock
8+
9+
import pytest
10+
11+
src_dir = str(Path(__file__).parent.parent.joinpath("src").absolute())
12+
if src_dir not in sys.path:
13+
sys.path.insert(0, src_dir)
14+
15+
16+
@pytest.fixture
17+
def mock_helper():
18+
"""Return a mock OpenCTI helper."""
19+
helper = Mock()
20+
helper.connector_logger = Mock()
21+
helper.api = Mock()
22+
helper.api.work = Mock()
23+
helper.stix2_create_bundle = Mock(return_value='{"type": "bundle", "objects": []}')
24+
helper.send_stix2_bundle = Mock()
25+
return helper
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pytest>=8.0.0
2+
pytest-mock>=3.10.0
3+
pycti==7.260430.0
4+
connectors-sdk @ git+https://github.com/OpenCTI-Platform/connectors.git@master#subdirectory=connectors-sdk
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
"""Tests for SublimeClient API client."""
2+
3+
from unittest.mock import Mock, patch
4+
5+
import pytest
6+
from pydantic import HttpUrl, SecretStr
7+
from sublime_client.api_client import SublimeClient
8+
9+
10+
@pytest.fixture
11+
def client(mock_helper):
12+
"""Create a SublimeClient instance with mocked dependencies."""
13+
return SublimeClient(
14+
helper=mock_helper,
15+
base_url=HttpUrl("https://platform.sublime.security"),
16+
api_key=SecretStr("test-api-key"),
17+
)
18+
19+
20+
class TestSublimeClientInit:
21+
"""Test client initialization."""
22+
23+
def test_init_sets_auth_header(self, mock_helper):
24+
client = SublimeClient(
25+
helper=mock_helper,
26+
base_url=HttpUrl("https://platform.sublime.security"),
27+
api_key=SecretStr("my-secret-token"),
28+
)
29+
assert client.session.headers["Authorization"] == "Bearer my-secret-token"
30+
assert client.session.headers["Accept"] == "application/json"
31+
32+
def test_init_sets_base_url(self, mock_helper):
33+
client = SublimeClient(
34+
helper=mock_helper,
35+
base_url=HttpUrl("https://custom.api.example.com"),
36+
api_key=SecretStr("key"),
37+
)
38+
assert "custom.api.example.com" in client.base_url.unicode_string()
39+
40+
41+
class TestGetGroupIds:
42+
"""Test get_group_ids method."""
43+
44+
def test_returns_group_ids(self, client):
45+
mock_response = Mock()
46+
mock_response.ok = True
47+
mock_response.json.return_value = {
48+
"all_group_canonical_ids": ["id-1", "id-2", "id-3"]
49+
}
50+
51+
with patch.object(client, "_request_data", return_value=mock_response):
52+
result = client.get_group_ids(
53+
"2026-01-01T00:00:00Z", "2026-01-02T00:00:00Z"
54+
)
55+
56+
assert result == ["id-1", "id-2", "id-3"]
57+
58+
def test_returns_empty_list_when_no_ids(self, client):
59+
mock_response = Mock()
60+
mock_response.ok = True
61+
mock_response.json.return_value = {"all_group_canonical_ids": None}
62+
63+
with patch.object(client, "_request_data", return_value=mock_response):
64+
result = client.get_group_ids(
65+
"2026-01-01T00:00:00Z", "2026-01-02T00:00:00Z"
66+
)
67+
68+
assert result == []
69+
70+
def test_uses_correct_params(self, client):
71+
mock_response = Mock()
72+
mock_response.ok = True
73+
mock_response.json.return_value = {"all_group_canonical_ids": []}
74+
75+
with patch.object(
76+
client, "_request_data", return_value=mock_response
77+
) as mock_req:
78+
client.get_group_ids("2026-01-01T00:00:00Z", "2026-01-02T00:00:00Z")
79+
80+
call_kwargs = mock_req.call_args
81+
assert "messages/groups" in call_kwargs.kwargs.get(
82+
"api_url", call_kwargs[1].get("api_url", "")
83+
)
84+
params = call_kwargs.kwargs.get("params", call_kwargs[1].get("params", {}))
85+
assert params["flagged__eq"] is True
86+
assert params["created_at__gte"] == "2026-01-01T00:00:00Z"
87+
assert params["created_at__lt"] == "2026-01-02T00:00:00Z"
88+
89+
def test_builds_url_with_v1(self, client):
90+
mock_response = Mock()
91+
mock_response.ok = True
92+
mock_response.json.return_value = {"all_group_canonical_ids": []}
93+
94+
with patch.object(
95+
client, "_request_data", return_value=mock_response
96+
) as mock_req:
97+
client.get_group_ids("2026-01-01T00:00:00Z", "2026-01-02T00:00:00Z")
98+
99+
api_url = mock_req.call_args[1].get("api_url") or mock_req.call_args.kwargs.get(
100+
"api_url"
101+
)
102+
assert "/v1/messages/groups" in api_url
103+
104+
105+
class TestGetSingleGroup:
106+
"""Test get_single_group method."""
107+
108+
def test_returns_group_data(self, client):
109+
mock_response = Mock()
110+
mock_response.ok = True
111+
mock_response.json.return_value = {
112+
"id": "group-123",
113+
"subjects": ["Phishing email"],
114+
"attack_score_verdict": "malicious",
115+
}
116+
117+
with patch.object(client, "_request_data", return_value=mock_response):
118+
result = client.get_single_group("group-123")
119+
120+
assert result["id"] == "group-123"
121+
assert result["attack_score_verdict"] == "malicious"
122+
123+
def test_maps_data_model_to_mdm(self, client):
124+
mock_response = Mock()
125+
mock_response.ok = True
126+
mock_response.json.return_value = {
127+
"id": "group-123",
128+
"data_model": {"sender": {"email": {"email": "evil@phish.com"}}},
129+
}
130+
131+
with patch.object(client, "_request_data", return_value=mock_response):
132+
result = client.get_single_group("group-123")
133+
134+
assert "MDM" in result
135+
assert result["MDM"]["sender"]["email"]["email"] == "evil@phish.com"
136+
137+
def test_does_not_overwrite_existing_mdm(self, client):
138+
mock_response = Mock()
139+
mock_response.ok = True
140+
mock_response.json.return_value = {
141+
"id": "group-123",
142+
"data_model": {"new": "data"},
143+
"MDM": {"existing": "data"},
144+
}
145+
146+
with patch.object(client, "_request_data", return_value=mock_response):
147+
result = client.get_single_group("group-123")
148+
149+
assert result["MDM"] == {"existing": "data"}
150+
151+
def test_returns_none_on_failure(self, client):
152+
mock_response = Mock()
153+
mock_response.ok = False
154+
mock_response.status_code = 404
155+
mock_response.text = "Not found"
156+
157+
with patch.object(client, "_request_data", return_value=mock_response):
158+
result = client.get_single_group("nonexistent")
159+
160+
assert result is None
161+
162+
def test_returns_none_on_exception(self, client):
163+
with patch.object(
164+
client, "_request_data", side_effect=Exception("network error")
165+
):
166+
result = client.get_single_group("group-123")
167+
168+
assert result is None
169+
170+
171+
class TestRequestData:
172+
"""Test the internal _request_data method."""
173+
174+
def test_successful_request(self, client):
175+
mock_response = Mock()
176+
mock_response.raise_for_status = Mock()
177+
178+
with patch.object(client.session, "get", return_value=mock_response):
179+
result = client._request_data("https://api.example.com/v1/test")
180+
181+
assert result == mock_response
182+
183+
def test_request_timeout(self, client):
184+
with patch.object(client.session, "get") as mock_get:
185+
client._request_data("https://api.example.com/v1/test")
186+
mock_get.assert_called_once_with(
187+
"https://api.example.com/v1/test", params=None, timeout=30
188+
)
189+
190+
def test_request_exception_returns_none(self, client):
191+
import requests
192+
193+
with patch.object(
194+
client.session, "get", side_effect=requests.RequestException("timeout")
195+
):
196+
result = client._request_data("https://api.example.com/v1/test")
197+
198+
assert result is None

0 commit comments

Comments
 (0)