hackerrank 2026.7.19__tar.gz → 2026.7.30__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/CHANGELOG.rst +7 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/PKG-INFO +9 -9
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/pyproject.toml +10 -8
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/src/hackerrank/_dict_types.py +2 -2
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/src/hackerrank/async_client.py +19 -23
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/src/hackerrank/client.py +19 -23
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/src/hackerrank/types.py +9 -5
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/src/hackerrank.egg-info/PKG-INFO +9 -9
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/src/hackerrank.egg-info/requires.txt +8 -8
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/src/hackerrank.egg-info/scm_file_list.json +31 -31
- hackerrank-2026.7.30/src/hackerrank.egg-info/scm_version.json +8 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/tests/conftest.py +13 -1
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/tests/test_endpoints.py +157 -22
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/tests/test_types.py +7 -2
- hackerrank-2026.7.19/src/hackerrank.egg-info/scm_version.json +0 -8
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/.git_archival.txt +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/.gitattributes +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/.github/dependabot.yml +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/.github/workflows/ci.yml +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/.github/workflows/dependabot-merge.yml +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/.github/workflows/lint.yml +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/.github/workflows/publish-site.yml +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/.github/workflows/release.yml +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/.gitignore +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/.pre-commit-config.yaml +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/.prettierrc +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/LICENSE +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/README.rst +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/conftest.py +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/docs/source/__init__.py +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/docs/source/api-reference.rst +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/docs/source/changelog.rst +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/docs/source/conf.py +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/docs/source/contributing.rst +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/docs/source/index.rst +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/docs/source/openapi-spec.rst +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/docs/source/release-process.rst +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/docs/source/unreleased.rst +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/docs/towncrier_template.rst.jinja +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/newsfragments/.gitkeep +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/openapi.json +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/setup.cfg +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/spelling_private_dict.txt +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/src/hackerrank/__init__.py +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/src/hackerrank/exceptions.py +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/src/hackerrank/py.typed +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/src/hackerrank/transports.py +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/src/hackerrank.egg-info/SOURCES.txt +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/src/hackerrank.egg-info/dependency_links.txt +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/src/hackerrank.egg-info/not-zip-safe +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/src/hackerrank.egg-info/top_level.txt +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/tests/__init__.py +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/tests/test_async_client.py +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/tests/test_client.py +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/tests/test_coverage_gaps.py +0 -0
- {hackerrank-2026.7.19 → hackerrank-2026.7.30}/zizmor.yml +0 -0
|
@@ -3,6 +3,13 @@ Changelog
|
|
|
3
3
|
|
|
4
4
|
.. towncrier release notes start
|
|
5
5
|
|
|
6
|
+
2026.07.30
|
|
7
|
+
----------
|
|
8
|
+
|
|
9
|
+
- Align interview-template create and update requests with the current API,
|
|
10
|
+
return updated templates, and parse live string template IDs and integer
|
|
11
|
+
question IDs.
|
|
12
|
+
|
|
6
13
|
2026.07.19
|
|
7
14
|
----------
|
|
8
15
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hackerrank
|
|
3
|
-
Version: 2026.7.
|
|
3
|
+
Version: 2026.7.30
|
|
4
4
|
Summary: Interact with the HackerRank for Work API.
|
|
5
5
|
Author-email: Adam Dangoor <adamdangoor@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -25,19 +25,19 @@ Requires-Dist: beartype==0.22.9; extra == "dev"
|
|
|
25
25
|
Requires-Dist: check-manifest==0.51; extra == "dev"
|
|
26
26
|
Requires-Dist: deptry==0.25.1; extra == "dev"
|
|
27
27
|
Requires-Dist: doc8==2.0.0; extra == "dev"
|
|
28
|
-
Requires-Dist: doccmd==2026.
|
|
28
|
+
Requires-Dist: doccmd==2026.7.19; extra == "dev"
|
|
29
29
|
Requires-Dist: furo==2025.12.19; extra == "dev"
|
|
30
30
|
Requires-Dist: interrogate==1.7.0; extra == "dev"
|
|
31
31
|
Requires-Dist: mypy[faster-cache]==2.3.0; extra == "dev"
|
|
32
|
-
Requires-Dist: mypy-strict-kwargs==2026.
|
|
32
|
+
Requires-Dist: mypy-strict-kwargs==2026.7.19.1; extra == "dev"
|
|
33
33
|
Requires-Dist: openapi-mock==2026.3.2; extra == "dev"
|
|
34
|
-
Requires-Dist: prek==0.4.
|
|
34
|
+
Requires-Dist: prek==0.4.11; extra == "dev"
|
|
35
35
|
Requires-Dist: pydocstringformatter==1.0.0; extra == "dev"
|
|
36
36
|
Requires-Dist: pydocstyle==6.3; extra == "dev"
|
|
37
37
|
Requires-Dist: pygments==2.20.0; extra == "dev"
|
|
38
38
|
Requires-Dist: pylint[spelling]==4.0.6; extra == "dev"
|
|
39
39
|
Requires-Dist: pylint-per-file-ignores==3.2.1; extra == "dev"
|
|
40
|
-
Requires-Dist: pyproject-fmt==2.
|
|
40
|
+
Requires-Dist: pyproject-fmt==2.26.0; extra == "dev"
|
|
41
41
|
Requires-Dist: pyrefly==1.1.1; extra == "dev"
|
|
42
42
|
Requires-Dist: pyright==1.1.411; extra == "dev"
|
|
43
43
|
Requires-Dist: pyroma==5.0.1; extra == "dev"
|
|
@@ -47,7 +47,7 @@ Requires-Dist: pytest-beartype-tests==2026.4.26; extra == "dev"
|
|
|
47
47
|
Requires-Dist: pytest-cov==7.1.0; extra == "dev"
|
|
48
48
|
Requires-Dist: pyyaml==6.0.3; extra == "dev"
|
|
49
49
|
Requires-Dist: respx==0.23.1; extra == "dev"
|
|
50
|
-
Requires-Dist: ruff==0.
|
|
50
|
+
Requires-Dist: ruff==0.16.0; extra == "dev"
|
|
51
51
|
Requires-Dist: shellcheck-py==0.11.0.1; extra == "dev"
|
|
52
52
|
Requires-Dist: shfmt-py==4.0.0; extra == "dev"
|
|
53
53
|
Requires-Dist: sphinx==9.1.0; extra == "dev"
|
|
@@ -57,13 +57,13 @@ Requires-Dist: sphinx-pyproject==0.3.0; extra == "dev"
|
|
|
57
57
|
Requires-Dist: sphinx-substitution-extensions==2026.6.17; extra == "dev"
|
|
58
58
|
Requires-Dist: sphinxcontrib-spelling==8.0.2; extra == "dev"
|
|
59
59
|
Requires-Dist: sphinxcontrib-towncrier==0.5.0a0; extra == "dev"
|
|
60
|
-
Requires-Dist: strict-kwargs==2026.
|
|
60
|
+
Requires-Dist: strict-kwargs==2026.7.24; extra == "dev"
|
|
61
61
|
Requires-Dist: sybil==10.1.0; extra == "dev"
|
|
62
62
|
Requires-Dist: towncrier==25.8.0; extra == "dev"
|
|
63
|
-
Requires-Dist: ty==0.0.
|
|
63
|
+
Requires-Dist: ty==0.0.65; extra == "dev"
|
|
64
64
|
Requires-Dist: vulture==2.16; extra == "dev"
|
|
65
65
|
Requires-Dist: yamlfix==1.19.1; extra == "dev"
|
|
66
|
-
Requires-Dist: zizmor==1.
|
|
66
|
+
Requires-Dist: zizmor==1.28.0; extra == "dev"
|
|
67
67
|
Provides-Extra: release
|
|
68
68
|
Requires-Dist: check-wheel-contents==0.6.3; extra == "release"
|
|
69
69
|
Requires-Dist: towncrier==25.8.0; extra == "release"
|
|
@@ -41,19 +41,19 @@ optional-dependencies.dev = [
|
|
|
41
41
|
"check-manifest==0.51",
|
|
42
42
|
"deptry==0.25.1",
|
|
43
43
|
"doc8==2.0.0",
|
|
44
|
-
"doccmd==2026.
|
|
44
|
+
"doccmd==2026.7.19",
|
|
45
45
|
"furo==2025.12.19",
|
|
46
46
|
"interrogate==1.7.0",
|
|
47
47
|
"mypy[faster-cache]==2.3.0",
|
|
48
|
-
"mypy-strict-kwargs==2026.
|
|
48
|
+
"mypy-strict-kwargs==2026.7.19.1",
|
|
49
49
|
"openapi-mock==2026.3.2",
|
|
50
|
-
"prek==0.4.
|
|
50
|
+
"prek==0.4.11",
|
|
51
51
|
"pydocstringformatter==1.0.0",
|
|
52
52
|
"pydocstyle==6.3",
|
|
53
53
|
"pygments==2.20.0",
|
|
54
54
|
"pylint[spelling]==4.0.6",
|
|
55
55
|
"pylint-per-file-ignores==3.2.1",
|
|
56
|
-
"pyproject-fmt==2.
|
|
56
|
+
"pyproject-fmt==2.26.0",
|
|
57
57
|
"pyrefly==1.1.1",
|
|
58
58
|
"pyright==1.1.411",
|
|
59
59
|
"pyroma==5.0.1",
|
|
@@ -63,7 +63,7 @@ optional-dependencies.dev = [
|
|
|
63
63
|
"pytest-cov==7.1.0",
|
|
64
64
|
"pyyaml==6.0.3",
|
|
65
65
|
"respx==0.23.1",
|
|
66
|
-
"ruff==0.
|
|
66
|
+
"ruff==0.16.0",
|
|
67
67
|
# We add shellcheck-py not only for shell scripts and shell code blocks,
|
|
68
68
|
# but also because having it installed means that ``actionlint-py`` will
|
|
69
69
|
# use it to lint shell commands in GitHub workflow files.
|
|
@@ -78,13 +78,13 @@ optional-dependencies.dev = [
|
|
|
78
78
|
# ``sphinxcontrib-towncrier`` renders unreleased news fragments
|
|
79
79
|
# into docs/source/unreleased.rst during Sphinx builds.
|
|
80
80
|
"sphinxcontrib-towncrier==0.5.0a0",
|
|
81
|
-
"strict-kwargs==2026.
|
|
81
|
+
"strict-kwargs==2026.7.24",
|
|
82
82
|
"sybil==10.1.0",
|
|
83
83
|
"towncrier==25.8.0",
|
|
84
|
-
"ty==0.0.
|
|
84
|
+
"ty==0.0.65",
|
|
85
85
|
"vulture==2.16",
|
|
86
86
|
"yamlfix==1.19.1",
|
|
87
|
-
"zizmor==1.
|
|
87
|
+
"zizmor==1.28.0",
|
|
88
88
|
]
|
|
89
89
|
optional-dependencies.release = [ "check-wheel-contents==0.6.3", "towncrier==25.8.0" ]
|
|
90
90
|
urls.Documentation = "https://adamtheturtle.github.io/hackerrank/"
|
|
@@ -118,6 +118,8 @@ lint.select = [
|
|
|
118
118
|
lint.ignore = [
|
|
119
119
|
# Ruff warns that this conflicts with the formatter.
|
|
120
120
|
"COM812",
|
|
121
|
+
# Copyright headers are not required.
|
|
122
|
+
"CPY001",
|
|
121
123
|
# Allow our chosen docstring line-style - pydocstringformatter handles formatting
|
|
122
124
|
# but doesn't enforce D205 (blank line after summary) or D212 (summary on first line).
|
|
123
125
|
"D205",
|
|
@@ -47,14 +47,14 @@ class InterviewTranscriptDict(TypedDict):
|
|
|
47
47
|
class InterviewTemplateDict(TypedDict):
|
|
48
48
|
"""A HackerRank interview template."""
|
|
49
49
|
|
|
50
|
-
id: int
|
|
50
|
+
id: int | str
|
|
51
51
|
name: str
|
|
52
52
|
created_at: NotRequired[str]
|
|
53
53
|
status: NotRequired[int]
|
|
54
54
|
user: NotRequired[int]
|
|
55
55
|
roles: NotRequired[list[str]]
|
|
56
56
|
team_share: NotRequired[int]
|
|
57
|
-
questions: NotRequired[list[str]]
|
|
57
|
+
questions: NotRequired[list[int | str]]
|
|
58
58
|
scorecard: NotRequired[int]
|
|
59
59
|
import_template: NotRequired[bool]
|
|
60
60
|
editor_access: NotRequired[bool]
|
|
@@ -510,19 +510,17 @@ class AsyncInterviewTemplatesNamespace(_AsyncNamespace):
|
|
|
510
510
|
self,
|
|
511
511
|
*,
|
|
512
512
|
name: str,
|
|
513
|
-
|
|
513
|
+
role_id: str | None = None,
|
|
514
514
|
team_share: int | None = None,
|
|
515
|
-
|
|
516
|
-
scorecard: int | None = None,
|
|
515
|
+
question_ids: Sequence[int] | None = None,
|
|
517
516
|
) -> InterviewTemplate:
|
|
518
517
|
"""Create an interview template.
|
|
519
518
|
|
|
520
519
|
Args:
|
|
521
520
|
name: The template name.
|
|
522
|
-
|
|
521
|
+
role_id: Role unique id for the template.
|
|
523
522
|
team_share: Team-share flag.
|
|
524
|
-
|
|
525
|
-
scorecard: Scorecard id.
|
|
523
|
+
question_ids: Question ids to add to the template.
|
|
526
524
|
|
|
527
525
|
Returns:
|
|
528
526
|
The created interview template.
|
|
@@ -530,12 +528,11 @@ class AsyncInterviewTemplatesNamespace(_AsyncNamespace):
|
|
|
530
528
|
body = _drop_none(
|
|
531
529
|
{
|
|
532
530
|
"name": name,
|
|
533
|
-
"
|
|
531
|
+
"role_id": role_id,
|
|
534
532
|
"team_share": team_share,
|
|
535
|
-
"
|
|
536
|
-
list(
|
|
533
|
+
"question_ids": (
|
|
534
|
+
list(question_ids) if question_ids is not None else None
|
|
537
535
|
),
|
|
538
|
-
"scorecard": scorecard,
|
|
539
536
|
},
|
|
540
537
|
)
|
|
541
538
|
response = await self._request(
|
|
@@ -569,37 +566,36 @@ class AsyncInterviewTemplatesNamespace(_AsyncNamespace):
|
|
|
569
566
|
*,
|
|
570
567
|
template_id: int | str,
|
|
571
568
|
name: str | None = None,
|
|
572
|
-
|
|
569
|
+
role_id: str | None = None,
|
|
573
570
|
team_share: int | None = None,
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
) -> None:
|
|
571
|
+
scorecard_id: int | None = None,
|
|
572
|
+
) -> InterviewTemplate:
|
|
577
573
|
"""Update an interview template.
|
|
578
574
|
|
|
579
575
|
Args:
|
|
580
576
|
template_id: The id of the template.
|
|
581
577
|
name: New name.
|
|
582
|
-
|
|
578
|
+
role_id: New role unique id.
|
|
583
579
|
team_share: New team-share flag.
|
|
584
|
-
|
|
585
|
-
|
|
580
|
+
scorecard_id: New scorecard id.
|
|
581
|
+
|
|
582
|
+
Returns:
|
|
583
|
+
The updated interview template.
|
|
586
584
|
"""
|
|
587
585
|
body = _drop_none(
|
|
588
586
|
{
|
|
589
587
|
"name": name,
|
|
590
|
-
"
|
|
588
|
+
"role_id": role_id,
|
|
591
589
|
"team_share": team_share,
|
|
592
|
-
"
|
|
593
|
-
list(questions) if questions is not None else None
|
|
594
|
-
),
|
|
595
|
-
"scorecard": scorecard,
|
|
590
|
+
"scorecard_id": scorecard_id,
|
|
596
591
|
},
|
|
597
592
|
)
|
|
598
|
-
await self._request(
|
|
593
|
+
response = await self._request(
|
|
599
594
|
method="PUT",
|
|
600
595
|
url=(f"{_API_V3}/interview_templates/{template_id}"),
|
|
601
596
|
json=body,
|
|
602
597
|
)
|
|
598
|
+
return InterviewTemplate.from_dict(data=response.json())
|
|
603
599
|
|
|
604
600
|
async def delete(self, *, template_id: int | str) -> None:
|
|
605
601
|
"""Delete an interview template.
|
|
@@ -497,19 +497,17 @@ class InterviewTemplatesNamespace(_Namespace):
|
|
|
497
497
|
self,
|
|
498
498
|
*,
|
|
499
499
|
name: str,
|
|
500
|
-
|
|
500
|
+
role_id: str | None = None,
|
|
501
501
|
team_share: int | None = None,
|
|
502
|
-
|
|
503
|
-
scorecard: int | None = None,
|
|
502
|
+
question_ids: Sequence[int] | None = None,
|
|
504
503
|
) -> InterviewTemplate:
|
|
505
504
|
"""Create an interview template.
|
|
506
505
|
|
|
507
506
|
Args:
|
|
508
507
|
name: The template name.
|
|
509
|
-
|
|
508
|
+
role_id: Role unique id for the template.
|
|
510
509
|
team_share: Team-share flag.
|
|
511
|
-
|
|
512
|
-
scorecard: Scorecard id.
|
|
510
|
+
question_ids: Question ids to add to the template.
|
|
513
511
|
|
|
514
512
|
Returns:
|
|
515
513
|
The created interview template.
|
|
@@ -517,12 +515,11 @@ class InterviewTemplatesNamespace(_Namespace):
|
|
|
517
515
|
body = _drop_none(
|
|
518
516
|
{
|
|
519
517
|
"name": name,
|
|
520
|
-
"
|
|
518
|
+
"role_id": role_id,
|
|
521
519
|
"team_share": team_share,
|
|
522
|
-
"
|
|
523
|
-
list(
|
|
520
|
+
"question_ids": (
|
|
521
|
+
list(question_ids) if question_ids is not None else None
|
|
524
522
|
),
|
|
525
|
-
"scorecard": scorecard,
|
|
526
523
|
},
|
|
527
524
|
)
|
|
528
525
|
response = self._request(
|
|
@@ -552,37 +549,36 @@ class InterviewTemplatesNamespace(_Namespace):
|
|
|
552
549
|
*,
|
|
553
550
|
template_id: int | str,
|
|
554
551
|
name: str | None = None,
|
|
555
|
-
|
|
552
|
+
role_id: str | None = None,
|
|
556
553
|
team_share: int | None = None,
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
) -> None:
|
|
554
|
+
scorecard_id: int | None = None,
|
|
555
|
+
) -> InterviewTemplate:
|
|
560
556
|
"""Update an interview template.
|
|
561
557
|
|
|
562
558
|
Args:
|
|
563
559
|
template_id: The id of the template.
|
|
564
560
|
name: New name.
|
|
565
|
-
|
|
561
|
+
role_id: New role unique id.
|
|
566
562
|
team_share: New team-share flag.
|
|
567
|
-
|
|
568
|
-
|
|
563
|
+
scorecard_id: New scorecard id.
|
|
564
|
+
|
|
565
|
+
Returns:
|
|
566
|
+
The updated interview template.
|
|
569
567
|
"""
|
|
570
568
|
body = _drop_none(
|
|
571
569
|
{
|
|
572
570
|
"name": name,
|
|
573
|
-
"
|
|
571
|
+
"role_id": role_id,
|
|
574
572
|
"team_share": team_share,
|
|
575
|
-
"
|
|
576
|
-
list(questions) if questions is not None else None
|
|
577
|
-
),
|
|
578
|
-
"scorecard": scorecard,
|
|
573
|
+
"scorecard_id": scorecard_id,
|
|
579
574
|
},
|
|
580
575
|
)
|
|
581
|
-
self._request(
|
|
576
|
+
response = self._request(
|
|
582
577
|
method="PUT",
|
|
583
578
|
url=(f"{_API_V3}/interview_templates/{template_id}"),
|
|
584
579
|
json=body,
|
|
585
580
|
)
|
|
581
|
+
return InterviewTemplate.from_dict(data=response.json())
|
|
586
582
|
|
|
587
583
|
def delete(self, *, template_id: int | str) -> None:
|
|
588
584
|
"""Delete an interview template.
|
|
@@ -30,7 +30,7 @@ from hackerrank._dict_types import (
|
|
|
30
30
|
)
|
|
31
31
|
|
|
32
32
|
type JSONValue = (
|
|
33
|
-
str | int | float | bool |
|
|
33
|
+
str | int | float | bool | Sequence[Any] | Mapping[str, Any] | None
|
|
34
34
|
)
|
|
35
35
|
"""A JSON-compatible value.
|
|
36
36
|
|
|
@@ -278,14 +278,14 @@ class InterviewTranscript:
|
|
|
278
278
|
class InterviewTemplate:
|
|
279
279
|
"""A HackerRank interview template."""
|
|
280
280
|
|
|
281
|
-
id:
|
|
281
|
+
id: str
|
|
282
282
|
name: str
|
|
283
283
|
created_at: str | None = None
|
|
284
284
|
status: int | None = None
|
|
285
285
|
user: int | None = None
|
|
286
286
|
roles: list[str] | None = None
|
|
287
287
|
team_share: int | None = None
|
|
288
|
-
questions: list[
|
|
288
|
+
questions: list[int] | None = None
|
|
289
289
|
scorecard: int | None = None
|
|
290
290
|
import_template: bool | None = None
|
|
291
291
|
editor_access: bool | None = None
|
|
@@ -301,14 +301,18 @@ class InterviewTemplate:
|
|
|
301
301
|
A new instance.
|
|
302
302
|
"""
|
|
303
303
|
return cls(
|
|
304
|
-
id=data["id"],
|
|
304
|
+
id=str(object=data["id"]),
|
|
305
305
|
name=data["name"],
|
|
306
306
|
created_at=data.get("created_at"),
|
|
307
307
|
status=data.get("status"),
|
|
308
308
|
user=data.get("user"),
|
|
309
309
|
roles=data.get("roles"),
|
|
310
310
|
team_share=data.get("team_share"),
|
|
311
|
-
questions=
|
|
311
|
+
questions=(
|
|
312
|
+
[int(question_id) for question_id in data["questions"]]
|
|
313
|
+
if "questions" in data
|
|
314
|
+
else None
|
|
315
|
+
),
|
|
312
316
|
scorecard=data.get("scorecard"),
|
|
313
317
|
import_template=data.get("import_template"),
|
|
314
318
|
editor_access=data.get("editor_access"),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hackerrank
|
|
3
|
-
Version: 2026.7.
|
|
3
|
+
Version: 2026.7.30
|
|
4
4
|
Summary: Interact with the HackerRank for Work API.
|
|
5
5
|
Author-email: Adam Dangoor <adamdangoor@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -25,19 +25,19 @@ Requires-Dist: beartype==0.22.9; extra == "dev"
|
|
|
25
25
|
Requires-Dist: check-manifest==0.51; extra == "dev"
|
|
26
26
|
Requires-Dist: deptry==0.25.1; extra == "dev"
|
|
27
27
|
Requires-Dist: doc8==2.0.0; extra == "dev"
|
|
28
|
-
Requires-Dist: doccmd==2026.
|
|
28
|
+
Requires-Dist: doccmd==2026.7.19; extra == "dev"
|
|
29
29
|
Requires-Dist: furo==2025.12.19; extra == "dev"
|
|
30
30
|
Requires-Dist: interrogate==1.7.0; extra == "dev"
|
|
31
31
|
Requires-Dist: mypy[faster-cache]==2.3.0; extra == "dev"
|
|
32
|
-
Requires-Dist: mypy-strict-kwargs==2026.
|
|
32
|
+
Requires-Dist: mypy-strict-kwargs==2026.7.19.1; extra == "dev"
|
|
33
33
|
Requires-Dist: openapi-mock==2026.3.2; extra == "dev"
|
|
34
|
-
Requires-Dist: prek==0.4.
|
|
34
|
+
Requires-Dist: prek==0.4.11; extra == "dev"
|
|
35
35
|
Requires-Dist: pydocstringformatter==1.0.0; extra == "dev"
|
|
36
36
|
Requires-Dist: pydocstyle==6.3; extra == "dev"
|
|
37
37
|
Requires-Dist: pygments==2.20.0; extra == "dev"
|
|
38
38
|
Requires-Dist: pylint[spelling]==4.0.6; extra == "dev"
|
|
39
39
|
Requires-Dist: pylint-per-file-ignores==3.2.1; extra == "dev"
|
|
40
|
-
Requires-Dist: pyproject-fmt==2.
|
|
40
|
+
Requires-Dist: pyproject-fmt==2.26.0; extra == "dev"
|
|
41
41
|
Requires-Dist: pyrefly==1.1.1; extra == "dev"
|
|
42
42
|
Requires-Dist: pyright==1.1.411; extra == "dev"
|
|
43
43
|
Requires-Dist: pyroma==5.0.1; extra == "dev"
|
|
@@ -47,7 +47,7 @@ Requires-Dist: pytest-beartype-tests==2026.4.26; extra == "dev"
|
|
|
47
47
|
Requires-Dist: pytest-cov==7.1.0; extra == "dev"
|
|
48
48
|
Requires-Dist: pyyaml==6.0.3; extra == "dev"
|
|
49
49
|
Requires-Dist: respx==0.23.1; extra == "dev"
|
|
50
|
-
Requires-Dist: ruff==0.
|
|
50
|
+
Requires-Dist: ruff==0.16.0; extra == "dev"
|
|
51
51
|
Requires-Dist: shellcheck-py==0.11.0.1; extra == "dev"
|
|
52
52
|
Requires-Dist: shfmt-py==4.0.0; extra == "dev"
|
|
53
53
|
Requires-Dist: sphinx==9.1.0; extra == "dev"
|
|
@@ -57,13 +57,13 @@ Requires-Dist: sphinx-pyproject==0.3.0; extra == "dev"
|
|
|
57
57
|
Requires-Dist: sphinx-substitution-extensions==2026.6.17; extra == "dev"
|
|
58
58
|
Requires-Dist: sphinxcontrib-spelling==8.0.2; extra == "dev"
|
|
59
59
|
Requires-Dist: sphinxcontrib-towncrier==0.5.0a0; extra == "dev"
|
|
60
|
-
Requires-Dist: strict-kwargs==2026.
|
|
60
|
+
Requires-Dist: strict-kwargs==2026.7.24; extra == "dev"
|
|
61
61
|
Requires-Dist: sybil==10.1.0; extra == "dev"
|
|
62
62
|
Requires-Dist: towncrier==25.8.0; extra == "dev"
|
|
63
|
-
Requires-Dist: ty==0.0.
|
|
63
|
+
Requires-Dist: ty==0.0.65; extra == "dev"
|
|
64
64
|
Requires-Dist: vulture==2.16; extra == "dev"
|
|
65
65
|
Requires-Dist: yamlfix==1.19.1; extra == "dev"
|
|
66
|
-
Requires-Dist: zizmor==1.
|
|
66
|
+
Requires-Dist: zizmor==1.28.0; extra == "dev"
|
|
67
67
|
Provides-Extra: release
|
|
68
68
|
Requires-Dist: check-wheel-contents==0.6.3; extra == "release"
|
|
69
69
|
Requires-Dist: towncrier==25.8.0; extra == "release"
|
|
@@ -7,19 +7,19 @@ beartype==0.22.9
|
|
|
7
7
|
check-manifest==0.51
|
|
8
8
|
deptry==0.25.1
|
|
9
9
|
doc8==2.0.0
|
|
10
|
-
doccmd==2026.
|
|
10
|
+
doccmd==2026.7.19
|
|
11
11
|
furo==2025.12.19
|
|
12
12
|
interrogate==1.7.0
|
|
13
13
|
mypy[faster-cache]==2.3.0
|
|
14
|
-
mypy-strict-kwargs==2026.
|
|
14
|
+
mypy-strict-kwargs==2026.7.19.1
|
|
15
15
|
openapi-mock==2026.3.2
|
|
16
|
-
prek==0.4.
|
|
16
|
+
prek==0.4.11
|
|
17
17
|
pydocstringformatter==1.0.0
|
|
18
18
|
pydocstyle==6.3
|
|
19
19
|
pygments==2.20.0
|
|
20
20
|
pylint[spelling]==4.0.6
|
|
21
21
|
pylint-per-file-ignores==3.2.1
|
|
22
|
-
pyproject-fmt==2.
|
|
22
|
+
pyproject-fmt==2.26.0
|
|
23
23
|
pyrefly==1.1.1
|
|
24
24
|
pyright==1.1.411
|
|
25
25
|
pyroma==5.0.1
|
|
@@ -29,7 +29,7 @@ pytest-beartype-tests==2026.4.26
|
|
|
29
29
|
pytest-cov==7.1.0
|
|
30
30
|
pyyaml==6.0.3
|
|
31
31
|
respx==0.23.1
|
|
32
|
-
ruff==0.
|
|
32
|
+
ruff==0.16.0
|
|
33
33
|
shellcheck-py==0.11.0.1
|
|
34
34
|
shfmt-py==4.0.0
|
|
35
35
|
sphinx==9.1.0
|
|
@@ -39,13 +39,13 @@ sphinx-pyproject==0.3.0
|
|
|
39
39
|
sphinx-substitution-extensions==2026.6.17
|
|
40
40
|
sphinxcontrib-spelling==8.0.2
|
|
41
41
|
sphinxcontrib-towncrier==0.5.0a0
|
|
42
|
-
strict-kwargs==2026.
|
|
42
|
+
strict-kwargs==2026.7.24
|
|
43
43
|
sybil==10.1.0
|
|
44
44
|
towncrier==25.8.0
|
|
45
|
-
ty==0.0.
|
|
45
|
+
ty==0.0.65
|
|
46
46
|
vulture==2.16
|
|
47
47
|
yamlfix==1.19.1
|
|
48
|
-
zizmor==1.
|
|
48
|
+
zizmor==1.28.0
|
|
49
49
|
|
|
50
50
|
[release]
|
|
51
51
|
check-wheel-contents==0.6.3
|
|
@@ -1,49 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"files": [
|
|
3
|
-
".
|
|
4
|
-
".
|
|
5
|
-
"
|
|
3
|
+
"openapi.json",
|
|
4
|
+
".gitattributes",
|
|
5
|
+
".pre-commit-config.yaml",
|
|
6
6
|
"zizmor.yml",
|
|
7
|
-
"
|
|
7
|
+
"README.rst",
|
|
8
8
|
"CHANGELOG.rst",
|
|
9
|
-
".
|
|
10
|
-
".gitattributes",
|
|
9
|
+
".git_archival.txt",
|
|
11
10
|
"pyproject.toml",
|
|
12
|
-
".pre-commit-config.yaml",
|
|
13
11
|
"LICENSE",
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"src/hackerrank/types.py",
|
|
19
|
-
"src/hackerrank/exceptions.py",
|
|
20
|
-
"src/hackerrank/_dict_types.py",
|
|
21
|
-
"src/hackerrank/async_client.py",
|
|
22
|
-
"src/hackerrank/py.typed",
|
|
23
|
-
"src/hackerrank/__init__.py",
|
|
12
|
+
"spelling_private_dict.txt",
|
|
13
|
+
"conftest.py",
|
|
14
|
+
".prettierrc",
|
|
15
|
+
".gitignore",
|
|
24
16
|
".github/dependabot.yml",
|
|
25
|
-
".github/workflows/
|
|
17
|
+
".github/workflows/dependabot-merge.yml",
|
|
18
|
+
".github/workflows/lint.yml",
|
|
26
19
|
".github/workflows/ci.yml",
|
|
20
|
+
".github/workflows/publish-site.yml",
|
|
27
21
|
".github/workflows/release.yml",
|
|
28
|
-
".github/workflows/lint.yml",
|
|
29
|
-
".github/workflows/dependabot-merge.yml",
|
|
30
|
-
"newsfragments/.gitkeep",
|
|
31
|
-
"tests/conftest.py",
|
|
32
|
-
"tests/test_types.py",
|
|
33
|
-
"tests/test_endpoints.py",
|
|
34
|
-
"tests/test_client.py",
|
|
35
22
|
"tests/test_async_client.py",
|
|
36
|
-
"tests/test_coverage_gaps.py",
|
|
37
23
|
"tests/__init__.py",
|
|
24
|
+
"tests/test_client.py",
|
|
25
|
+
"tests/test_types.py",
|
|
26
|
+
"tests/test_coverage_gaps.py",
|
|
27
|
+
"tests/test_endpoints.py",
|
|
28
|
+
"tests/conftest.py",
|
|
29
|
+
"src/hackerrank/types.py",
|
|
30
|
+
"src/hackerrank/async_client.py",
|
|
31
|
+
"src/hackerrank/transports.py",
|
|
32
|
+
"src/hackerrank/py.typed",
|
|
33
|
+
"src/hackerrank/__init__.py",
|
|
34
|
+
"src/hackerrank/_dict_types.py",
|
|
35
|
+
"src/hackerrank/client.py",
|
|
36
|
+
"src/hackerrank/exceptions.py",
|
|
38
37
|
"docs/towncrier_template.rst.jinja",
|
|
38
|
+
"docs/source/index.rst",
|
|
39
|
+
"docs/source/contributing.rst",
|
|
39
40
|
"docs/source/conf.py",
|
|
41
|
+
"docs/source/changelog.rst",
|
|
42
|
+
"docs/source/__init__.py",
|
|
40
43
|
"docs/source/release-process.rst",
|
|
41
44
|
"docs/source/unreleased.rst",
|
|
42
|
-
"docs/source/index.rst",
|
|
43
|
-
"docs/source/changelog.rst",
|
|
44
|
-
"docs/source/contributing.rst",
|
|
45
|
-
"docs/source/openapi-spec.rst",
|
|
46
45
|
"docs/source/api-reference.rst",
|
|
47
|
-
"docs/source/
|
|
46
|
+
"docs/source/openapi-spec.rst",
|
|
47
|
+
"newsfragments/.gitkeep"
|
|
48
48
|
]
|
|
49
49
|
}
|
|
@@ -42,7 +42,11 @@ _INTERVIEW_OBJ = {
|
|
|
42
42
|
"status": "scheduled",
|
|
43
43
|
"url": "https://example.com/iv1",
|
|
44
44
|
}
|
|
45
|
-
_INTERVIEW_TEMPLATE_OBJ = {
|
|
45
|
+
_INTERVIEW_TEMPLATE_OBJ = {
|
|
46
|
+
"id": "template-1",
|
|
47
|
+
"name": "T",
|
|
48
|
+
"questions": [2687118],
|
|
49
|
+
}
|
|
46
50
|
_ENVIRONMENT_OBJ = {
|
|
47
51
|
"id": 92,
|
|
48
52
|
"name": "PERN",
|
|
@@ -248,6 +252,14 @@ def _response_for( # noqa: C901, PLR0911, PLR0912 # pylint: disable=too-comple
|
|
|
248
252
|
json=_SCIM_TEAM_OBJ,
|
|
249
253
|
)
|
|
250
254
|
if method == "PUT":
|
|
255
|
+
if re.fullmatch(
|
|
256
|
+
pattern=r"/x/api/v3/interview_templates/[^/]+",
|
|
257
|
+
string=path,
|
|
258
|
+
):
|
|
259
|
+
return httpx.Response(
|
|
260
|
+
status_code=200,
|
|
261
|
+
json=_INTERVIEW_TEMPLATE_OBJ,
|
|
262
|
+
)
|
|
251
263
|
if re.fullmatch(
|
|
252
264
|
pattern=r"/x/api/v3/questions/[^/]+/(custom_codestubs|generate)",
|
|
253
265
|
string=path,
|
|
@@ -8,10 +8,14 @@ populated. Responses are stubbed via the ``stub_router`` fixture in
|
|
|
8
8
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
|
+
import json
|
|
12
|
+
|
|
13
|
+
import httpx
|
|
11
14
|
import pytest
|
|
15
|
+
import respx
|
|
12
16
|
|
|
13
|
-
from hackerrank.async_client import AsyncHackerRank
|
|
14
|
-
from hackerrank.client import HackerRank
|
|
17
|
+
from hackerrank.async_client import AsyncHackerRank
|
|
18
|
+
from hackerrank.client import HackerRank
|
|
15
19
|
|
|
16
20
|
|
|
17
21
|
class TestSyncEndpoints:
|
|
@@ -68,22 +72,84 @@ class TestSyncEndpoints:
|
|
|
68
72
|
sync_client.interview_templates.create(name="t")
|
|
69
73
|
sync_client.interview_templates.create(
|
|
70
74
|
name="t",
|
|
71
|
-
|
|
75
|
+
role_id="dev",
|
|
72
76
|
team_share=1,
|
|
73
|
-
|
|
74
|
-
scorecard=2,
|
|
77
|
+
question_ids=[2687118],
|
|
75
78
|
)
|
|
76
|
-
sync_client.interview_templates.get(template_id=1)
|
|
77
|
-
sync_client.interview_templates.update(template_id=1)
|
|
79
|
+
sync_client.interview_templates.get(template_id="template-1")
|
|
80
|
+
sync_client.interview_templates.update(template_id="template-1")
|
|
78
81
|
sync_client.interview_templates.update(
|
|
79
|
-
template_id=1,
|
|
82
|
+
template_id="template-1",
|
|
80
83
|
name="t",
|
|
81
|
-
|
|
84
|
+
role_id="dev",
|
|
82
85
|
team_share=1,
|
|
83
|
-
|
|
84
|
-
scorecard=2,
|
|
86
|
+
scorecard_id=2,
|
|
85
87
|
)
|
|
86
|
-
sync_client.interview_templates.delete(template_id=1)
|
|
88
|
+
sync_client.interview_templates.delete(template_id="template-1")
|
|
89
|
+
|
|
90
|
+
@staticmethod
|
|
91
|
+
def test_interview_template_contract() -> None:
|
|
92
|
+
"""Use the current request and response fields for templates."""
|
|
93
|
+
requests: list[httpx.Request] = []
|
|
94
|
+
|
|
95
|
+
def template_response(request: httpx.Request) -> httpx.Response:
|
|
96
|
+
"""Record a request and return a live-shaped response."""
|
|
97
|
+
requests.append(request)
|
|
98
|
+
return httpx.Response(
|
|
99
|
+
status_code=201 if request.method == "POST" else 200,
|
|
100
|
+
json={
|
|
101
|
+
"id": "template-1",
|
|
102
|
+
"name": "Backend interview",
|
|
103
|
+
"questions": [2687118],
|
|
104
|
+
},
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
with respx.mock(assert_all_called=True) as router:
|
|
108
|
+
router.post(
|
|
109
|
+
url=(
|
|
110
|
+
"https://www.hackerrank.com/x/api/v3/interview_templates"
|
|
111
|
+
),
|
|
112
|
+
).mock(side_effect=template_response)
|
|
113
|
+
router.put(
|
|
114
|
+
url=(
|
|
115
|
+
"https://www.hackerrank.com/x/api/v3/"
|
|
116
|
+
"interview_templates/template-1"
|
|
117
|
+
),
|
|
118
|
+
).mock(side_effect=template_response)
|
|
119
|
+
with HackerRank(api_key="test-key") as client:
|
|
120
|
+
created = client.interview_templates.create(
|
|
121
|
+
name="Backend interview",
|
|
122
|
+
role_id="backend",
|
|
123
|
+
team_share=2,
|
|
124
|
+
question_ids=[2687118, 2687119],
|
|
125
|
+
)
|
|
126
|
+
updated = client.interview_templates.update(
|
|
127
|
+
template_id=created.id,
|
|
128
|
+
name="Senior backend interview",
|
|
129
|
+
role_id="senior-backend",
|
|
130
|
+
team_share=3,
|
|
131
|
+
scorecard_id=98765,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
create_request = requests[0]
|
|
135
|
+
assert json.loads(s=create_request.content) == {
|
|
136
|
+
"name": "Backend interview",
|
|
137
|
+
"role_id": "backend",
|
|
138
|
+
"team_share": 2,
|
|
139
|
+
"question_ids": [2687118, 2687119],
|
|
140
|
+
}
|
|
141
|
+
assert created.id == "template-1"
|
|
142
|
+
assert created.questions == [2687118]
|
|
143
|
+
|
|
144
|
+
update_request = requests[1]
|
|
145
|
+
assert json.loads(s=update_request.content) == {
|
|
146
|
+
"name": "Senior backend interview",
|
|
147
|
+
"role_id": "senior-backend",
|
|
148
|
+
"team_share": 3,
|
|
149
|
+
"scorecard_id": 98765,
|
|
150
|
+
}
|
|
151
|
+
assert updated.id == "template-1"
|
|
152
|
+
assert updated.questions == [2687118]
|
|
87
153
|
|
|
88
154
|
@staticmethod
|
|
89
155
|
def test_questions(sync_client: HackerRank) -> None:
|
|
@@ -526,22 +592,91 @@ class TestAsyncEndpoints:
|
|
|
526
592
|
await async_client.interview_templates.create(name="t")
|
|
527
593
|
await async_client.interview_templates.create(
|
|
528
594
|
name="t",
|
|
529
|
-
|
|
595
|
+
role_id="dev",
|
|
530
596
|
team_share=1,
|
|
531
|
-
|
|
532
|
-
|
|
597
|
+
question_ids=[2687118],
|
|
598
|
+
)
|
|
599
|
+
await async_client.interview_templates.get(
|
|
600
|
+
template_id="template-1",
|
|
533
601
|
)
|
|
534
|
-
await async_client.interview_templates.get(template_id=1)
|
|
535
|
-
await async_client.interview_templates.update(template_id=1)
|
|
536
602
|
await async_client.interview_templates.update(
|
|
537
|
-
template_id=1,
|
|
603
|
+
template_id="template-1",
|
|
604
|
+
)
|
|
605
|
+
await async_client.interview_templates.update(
|
|
606
|
+
template_id="template-1",
|
|
538
607
|
name="t",
|
|
539
|
-
|
|
608
|
+
role_id="dev",
|
|
540
609
|
team_share=1,
|
|
541
|
-
|
|
542
|
-
scorecard=2,
|
|
610
|
+
scorecard_id=2,
|
|
543
611
|
)
|
|
544
|
-
await async_client.interview_templates.delete(
|
|
612
|
+
await async_client.interview_templates.delete(
|
|
613
|
+
template_id="template-1",
|
|
614
|
+
)
|
|
615
|
+
|
|
616
|
+
@staticmethod
|
|
617
|
+
@pytest.mark.asyncio
|
|
618
|
+
async def test_interview_template_contract() -> None:
|
|
619
|
+
"""Use current template fields in the async client."""
|
|
620
|
+
requests: list[httpx.Request] = []
|
|
621
|
+
|
|
622
|
+
def template_response(request: httpx.Request) -> httpx.Response:
|
|
623
|
+
"""Record a request and return a live-shaped response."""
|
|
624
|
+
requests.append(request)
|
|
625
|
+
return httpx.Response(
|
|
626
|
+
status_code=201 if request.method == "POST" else 200,
|
|
627
|
+
json={
|
|
628
|
+
"id": "template-1",
|
|
629
|
+
"name": "Backend interview",
|
|
630
|
+
"questions": [2687118],
|
|
631
|
+
},
|
|
632
|
+
)
|
|
633
|
+
|
|
634
|
+
with respx.mock(assert_all_called=True) as router:
|
|
635
|
+
router.post(
|
|
636
|
+
url=(
|
|
637
|
+
"https://www.hackerrank.com/x/api/v3/interview_templates"
|
|
638
|
+
),
|
|
639
|
+
).mock(side_effect=template_response)
|
|
640
|
+
router.put(
|
|
641
|
+
url=(
|
|
642
|
+
"https://www.hackerrank.com/x/api/v3/"
|
|
643
|
+
"interview_templates/template-1"
|
|
644
|
+
),
|
|
645
|
+
).mock(side_effect=template_response)
|
|
646
|
+
async with AsyncHackerRank(api_key="test-key") as client:
|
|
647
|
+
created = await client.interview_templates.create(
|
|
648
|
+
name="Backend interview",
|
|
649
|
+
role_id="backend",
|
|
650
|
+
team_share=2,
|
|
651
|
+
question_ids=[2687118, 2687119],
|
|
652
|
+
)
|
|
653
|
+
updated = await client.interview_templates.update(
|
|
654
|
+
template_id=created.id,
|
|
655
|
+
name="Senior backend interview",
|
|
656
|
+
role_id="senior-backend",
|
|
657
|
+
team_share=3,
|
|
658
|
+
scorecard_id=98765,
|
|
659
|
+
)
|
|
660
|
+
|
|
661
|
+
create_request = requests[0]
|
|
662
|
+
assert json.loads(s=create_request.content) == {
|
|
663
|
+
"name": "Backend interview",
|
|
664
|
+
"role_id": "backend",
|
|
665
|
+
"team_share": 2,
|
|
666
|
+
"question_ids": [2687118, 2687119],
|
|
667
|
+
}
|
|
668
|
+
assert created.id == "template-1"
|
|
669
|
+
assert created.questions == [2687118]
|
|
670
|
+
|
|
671
|
+
update_request = requests[1]
|
|
672
|
+
assert json.loads(s=update_request.content) == {
|
|
673
|
+
"name": "Senior backend interview",
|
|
674
|
+
"role_id": "senior-backend",
|
|
675
|
+
"team_share": 3,
|
|
676
|
+
"scorecard_id": 98765,
|
|
677
|
+
}
|
|
678
|
+
assert updated.id == "template-1"
|
|
679
|
+
assert updated.questions == [2687118]
|
|
545
680
|
|
|
546
681
|
@staticmethod
|
|
547
682
|
@pytest.mark.asyncio
|
|
@@ -203,9 +203,14 @@ class TestFromDict:
|
|
|
203
203
|
def test_interview_template_from_dict() -> None:
|
|
204
204
|
"""``InterviewTemplate.from_dict`` populates the dataclass."""
|
|
205
205
|
template = InterviewTemplate.from_dict(
|
|
206
|
-
data={
|
|
206
|
+
data={
|
|
207
|
+
"id": "template-1",
|
|
208
|
+
"name": "Standard",
|
|
209
|
+
"questions": [2687118, "2687119"],
|
|
210
|
+
},
|
|
207
211
|
)
|
|
208
|
-
assert template.id == 1
|
|
212
|
+
assert template.id == "template-1"
|
|
213
|
+
assert template.questions == [2687118, 2687119]
|
|
209
214
|
|
|
210
215
|
@staticmethod
|
|
211
216
|
def test_interview_transcript_from_dict() -> None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|