Compare commits

...

319 Commits

Author SHA1 Message Date
3d2d759d6b build: bump version 1.17.1 -> 1.18.0 2025-07-03 22:55:51 +00:00
Jon
b73de03d27 Merge pull request #851 from nofusscomputing/850-fix-itim-validation-failure 2025-07-04 08:08:00 +09:30
Jon
a9e953812c feat(python): upgrade django 5.1.9 -> 5.1.10
ref: #851
2025-07-04 07:47:21 +09:30
Jon
4344265ed5 fix(itim): Correct config that is in the incorrect format
Users can input config that contains bytecode chars which inturn, makes the config entered a str. convert any config that is a str to a dict, the correct format.

ref: #851 fixes #850
2025-07-04 07:42:05 +09:30
4fb94bdd6d build: bump version 1.17.0 -> 1.17.1 2025-06-02 00:05:20 +00:00
Jon
e2582373ad fix(base): Add python metrics to prometheus exporter
ref: #436
2025-06-02 08:25:37 +09:30
309483ea08 build: bump version 1.16.0 -> 1.17.0 2025-05-16 10:31:55 +00:00
Jon
b2bc200eb1 Merge pull request #740 from nofusscomputing/feature-next-release 2025-05-16 19:46:31 +09:30
Jon
a0ab0deb2a chore: squash migrations to reduce amount
ref: #740
2025-05-16 19:29:02 +09:30
Jon
1f9491ce73 Merge pull request #761 from nofusscomputing/new-model-access-organization 2025-05-16 03:26:34 +09:30
Jon
1663f19b2a refactor(human_resources): Update Functional ViewSet to use PyTest for Employee Model
ref: #761 #730
2025-05-16 03:10:00 +09:30
Jon
07277862cf refactor(Access): Update Functional ViewSet to use PyTest for Person Model
ref: #761 #730
2025-05-16 03:09:49 +09:30
Jon
f1016bd9cc refactor(Access): Update Functional ViewSet to use PyTest for Entity Model
ref: #761 #730
2025-05-16 03:07:57 +09:30
Jon
c39f479b96 refactor(Access): Update Functional ViewSet to use PyTest for Contact Model
ref: #761 #730
2025-05-16 03:07:46 +09:30
Jon
a4d1a2bf76 test(access): Functional ViewSet Test Suite Company model
ref: #761 #760
2025-05-16 03:07:28 +09:30
Jon
8bba04305f refactor(Access): Update Functional Permission to use PyTest for Person Model
ref: #761 #730
2025-05-16 02:18:00 +09:30
Jon
516d6fc136 refactor(Access): Update Functional Permission to use PyTest for Entity Model
ref: #761 #730
2025-05-16 02:17:52 +09:30
Jon
2e096cb495 refactor(Access): Update Functional Permission to use PyTest for Contact Model
ref: #761 #730
2025-05-16 02:17:44 +09:30
Jon
3505f915c5 refactor(Access): Update Functional Serializer to use PyTest for Contact Model
ref: #761 #730
2025-05-16 02:17:31 +09:30
Jon
254cd02649 refactor(Access): Update Functional Serializer to use PyTest for Entity Model
ref: #761 #730
2025-05-16 02:17:21 +09:30
Jon
97e37f34a1 refactor(Access): Update Functional Serializer to use PyTest for Person Model
ref: #761 #730
2025-05-16 02:17:10 +09:30
Jon
ae6b66b270 refactor(human_resources): Update Functional Serializer to use PyTest for Employee Model
ref: #761 #730
2025-05-16 02:16:57 +09:30
Jon
7a1aecebd5 refactor(human_resources): Update Functional Permissions to use PyTest for Employee Model
ref: #761 #730
2025-05-16 02:16:49 +09:30
Jon
387ffc9ade refactor(human_resources): Update Functional Metadata to use PyTest for Employee Model
ref: #761 #730
2025-05-16 02:16:38 +09:30
Jon
d0c3537753 refactor(access): Update Functional Metadata to use PyTest for Person Model
ref: #761 #730
2025-05-16 02:16:19 +09:30
Jon
eb97bfbeeb refactor(access): Update Functional Metadata to use PyTest for Entity Model
ref: #761 #730
2025-05-16 02:16:09 +09:30
Jon
7fee33999f refactor(access): Update Functional Metadata to use PyTest for Contact Model
ref: #761 #730
2025-05-16 02:15:56 +09:30
Jon
99c0e92336 test(access): Functional Serializer Test Suite Company model
ref: #761 #760
2025-05-16 02:14:50 +09:30
Jon
46494c033c test(access): Functional Permissions Test Suite Company model
ref: #761 #760
2025-05-16 02:14:34 +09:30
Jon
85986ce13e test(access): Functional MetaData Test Suite Company model
ref: #761 #760
2025-05-16 02:14:23 +09:30
Jon
bdeae5b38b test(access): ViewSet Test Suite Company model
ref: #761 #760
2025-05-15 23:07:08 +09:30
Jon
edc7aedbe0 fix(api): Dont try to access attribute if not exist in common viewset
ref: #761
2025-05-15 23:03:49 +09:30
Jon
900e03791a test(access): API field render Test Suite Company model
ref: #761 #760
2025-05-15 22:18:45 +09:30
Jon
2a977bbf47 test(access): Model Test Suite Company model
ref: #761 #760
2025-05-15 22:18:29 +09:30
Jon
d29df73d05 refactor(access): Update Model Entity to use PyTest for Model Test Suite
ref: #761 #730
2025-05-15 22:17:36 +09:30
Jon
1150e1b047 refactor(access): Update Model Contact to use PyTest for Model Test Suite
ref: #761 #730
2025-05-15 22:16:56 +09:30
Jon
cf7eeb5bde refactor(access): Update Model Person to use PyTest for Model Test Suite
ref: #761 #730
2025-05-15 22:16:45 +09:30
Jon
c2a367bd28 refactor(human_resources): Update Model Employee to use PyTest for Model Test Suite
ref: #761 #730
2025-05-15 22:15:43 +09:30
Jon
3c19e9d4cf fix(api): Dont try to access attribute if not exist in common viewset
ref: #761
2025-05-15 20:19:43 +09:30
Jon
e295e53f86 refactor(human_resources): Update Model Employee to use PyTest API Fields Render
ref: #761 #730
2025-05-15 20:03:19 +09:30
Jon
be02061b94 refactor(access): Update Model Person to use PyTest API Fields Render
ref: #761 #730
2025-05-15 20:03:02 +09:30
Jon
08dbe1e35b refactor(access): Update Model Contact to use PyTest API Fields Render
ref: #761 #730
2025-05-15 20:02:50 +09:30
Jon
8e9dca56bc refactor(access): Update Model Entity to use PyTest API Fields Render
ref: #761 #730
2025-05-15 20:02:39 +09:30
Jon
2e9fe29e99 fix(api): Correct ViewSet Sub-Model lookup
ref: #761
2025-05-15 19:45:04 +09:30
Jon
b0a6f207cd docs(user): Add Conpany page
ref: #761 #760
2025-05-15 18:40:44 +09:30
Jon
a51a79a763 feat(access): model access.Company feature flag 2025-00008
ref: #761 #760
2025-05-15 18:34:51 +09:30
Jon
b2d1903009 feat(access): URL route for model access.Company
ref: #761 #760
2025-05-15 18:34:26 +09:30
Jon
53f99f620c feat(access): Migration for model access.Company
ref: #761 #760
2025-05-15 18:34:03 +09:30
Jon
312267567f feat(access): Serializer for model access.Company
ref: #761 #760
2025-05-15 18:33:52 +09:30
Jon
34b2571a2e feat(access): New model access.Company
ref: #761 #760
2025-05-15 18:33:21 +09:30
Jon
b79874d056 chore(access): Update tenant url
ref: #761 #505
2025-05-15 17:46:57 +09:30
Jon
27e42fac56 feat(access): Organization -> Tenant Permission Migration
ref: #761 #505
2025-05-15 17:46:00 +09:30
Jon
4124d5eb38 Merge pull request #756 from nofusscomputing/2025-05-14 2025-05-15 05:37:15 +09:30
Jon
bb8c6378bc chore: Add some tests to aid in planning
ref: #756 #758
2025-05-15 04:59:06 +09:30
Jon
c3109f1894 feat(docker): Serve a robots.txt file for NO indexing
ref: #756
2025-05-15 03:58:37 +09:30
Jon
59b4b5ff39 feat(access): Organization -> Tenant Permission Migration
ref: #756 #505
2025-05-15 02:45:03 +09:30
Jon
2d7335ff85 refactor(access): Rename model Organization -> Tenant
ref: #756 #505
2025-05-15 02:44:53 +09:30
Jon
2e49de8573 refactor(settings): Update all references to User to use get_user_model()
ref: #756 closes #755
2025-05-14 19:57:56 +09:30
Jon
a4772e3c25 refactor(project_management): Update all references to User to use get_user_model()
ref: #756 #755
2025-05-14 19:40:54 +09:30
Jon
de9937606e refactor(itam): Update all references to User to use get_user_model()
ref: #756 #755
2025-05-14 19:40:44 +09:30
Jon
1b749e9f1a refactor(devops): Update all references to User to use get_user_model()
ref: #756 #755
2025-05-14 18:48:56 +09:30
Jon
f0b3748596 Merge pull request #754 from nofusscomputing/test-ticket-comment-base 2025-05-14 05:11:27 +09:30
Jon
2ea42d10e6 refactor(core): Update all references to User to use get_user_model()
ref: #754 #755
2025-05-14 04:37:38 +09:30
Jon
55cf10d289 refactor(config_management): Update all references to User to use get_user_model()
ref: #754 #755
2025-05-14 04:10:29 +09:30
Jon
09ab52b971 refactor(assistance): Update all references to User to use get_user_model()
ref: #754 #755
2025-05-14 04:04:51 +09:30
Jon
c00aa6fa73 refactor(app): Update all references to User to use get_user_model()
ref: #754 #755
2025-05-14 03:59:15 +09:30
Jon
817c8d63e1 refactor(api): Update all references to User to use get_user_model()
ref: #754 #755
2025-05-14 03:55:29 +09:30
Jon
cce885d961 refactor(accounting): Update all references to User to use get_user_model()
ref: #754 #755
2025-05-14 03:49:23 +09:30
Jon
6d92c484cd refactor(access): Update all references to User to use get_user_model()
ref: #754 #704 #755
2025-05-14 03:45:40 +09:30
Jon
f5be3b0f8e feat(base): Add var AUTH_USER_MODEL to settings
ref: #754 #704 #755
2025-05-14 03:41:00 +09:30
Jon
85afbbc01e chore(test): introduce further randomness to class fixtures so as not to create duplicate errors
ref: #754
2025-05-14 00:21:55 +09:30
Jon
e0c0c69d35 chore(test): Ensure comment cleanup during ticket tests
ref: #754 #726
2025-05-13 23:58:18 +09:30
Jon
df82650931 fix(core): Only take action on ticket comment if view exists
ref: #754 #726
2025-05-13 23:40:35 +09:30
Jon
3196006bad feat(core): Add Action comments on ticket change
ref: #754 #723
2025-05-13 23:31:08 +09:30
Jon
820755b9e0 chore(test): introduce further randomness to class fixtures so as not to create duplicate errors
ref: #754
2025-05-13 23:30:19 +09:30
Jon
1946c7aa88 fix(api): Ensure multi-nested searching for sub-models works
ref: #754
2025-05-13 23:29:02 +09:30
Jon
7c35a2d427 test(core): Unit viewset Test Cases for TicketCommentAction model
ref: #754 #736
2025-05-13 19:52:04 +09:30
Jon
c4ee706591 test(core): Unit model Test Cases for TicketCommentAction model
ref: #754 #736
2025-05-13 19:51:39 +09:30
Jon
4ee5f349f0 test(core): Unit API Render Test Cases for TicketCommentAction model
ref: #754 #736
2025-05-13 19:51:29 +09:30
Jon
21a1974ba1 test(core): Interim Functional model Test Case TicketCommentAction
ref: #754 #736
2025-05-13 19:50:51 +09:30
Jon
969b4d22fc feat(core): Remove add, change and delete permissions for model TicketCommentAction from permission selector
ref: #754 #736
2025-05-13 19:42:31 +09:30
Jon
6169324ffd feat(core): Serializer for model TicketCommentAction
ref: #754 #736
2025-05-13 19:22:00 +09:30
Jon
003cad1f58 feat(core): Migrations for model TicketCommentAction
ref: #754 #736
2025-05-13 19:19:23 +09:30
Jon
c8df4b21a0 feat(core): New model TicketCommentAction
ref: #754 #736
2025-05-13 19:19:10 +09:30
Jon
7f49fb2d0e feat(core): Setup serializer to meet requirements
ref: #754 #726
2025-05-13 18:58:18 +09:30
Jon
1cb028c273 feat(core): Setup model to meet requirements
ref: #754 #726
2025-05-12 20:11:01 +09:30
Jon
4ad6c10a64 chore(core): Remove commented code
ref: #754 #726
2025-05-12 20:10:28 +09:30
Jon
899df95994 feat(api): Add exception logging to ViewSetCommon
ref: #754 #752
2025-05-12 19:55:37 +09:30
Jon
0fa7ffa34a chore: correct dev log path to be <app root>/log
ref: #754
2025-05-12 19:36:08 +09:30
Jon
c46a597828 Merge pull request #753 from nofusscomputing/test-ticket-base-model 2025-05-12 06:46:17 +09:30
Jon
764f1b20d9 test(core): Ensure that a ticket milestone comes from the same assigned project
ref: #744 #723
2025-05-12 05:26:02 +09:30
Jon
01b4a681da test(core): SKIP Tests TicketBase Description Slash command Checks
Required until the slash commands comment creation uses the new ticketcommentbase model.

ref: #744 #723 #564 #746 #747
2025-05-12 04:07:19 +09:30
Jon
16299d480e test(core): TicketBase Description Slash command Checks
ref: #744 #723
2025-05-12 04:05:53 +09:30
Jon
b0cb9f6fd0 chore(test): Add global support not to generate tests that are marked skip or class skip
ref: #753 #739
2025-05-12 04:04:32 +09:30
Jon
5b133c951d chore(test): Add a global fixture to simulate a viewset
ref: #753 #723 #730
2025-05-12 02:53:19 +09:30
Jon
e29a7ec0e2 test(core): TicketBase Remaining Serializer Chacks
ref: #753 #723
2025-05-12 02:52:34 +09:30
Jon
e7213d8a70 Merge pull request #744 from nofusscomputing/test-ticket-comment-base 2025-05-11 02:31:54 +09:30
Jon
4d7510ad3a feat(python): Upgrade DRF Spectacular 0.27.2 -> 0.28.0
ref: #744
2025-05-11 01:48:46 +09:30
Jon
d8ef918a67 feat(python): Upgrade DRF 3.15.2 -> 3.16.0
ref: #744
2025-05-11 01:41:30 +09:30
Jon
cb49d0fbf7 feat(core): When processing slash command duration, cater for new ticket models
ref: #744 #728 #726 #746 #747
2025-05-10 23:42:40 +09:30
Jon
70c835eb93 test(core): Partial functional Model Test Suite covering some slash commande for TicketCommentSolution
ref: #744 #728
2025-05-10 23:40:41 +09:30
Jon
40b51f1a77 feat(api): Add Logging function to Common ViewSet
ref: #744 #436 #752
2025-05-10 23:39:22 +09:30
Jon
1bff76c637 feat(access): Add Logging function to Tenancy model
ref: #744 #436 #752
2025-05-10 23:38:58 +09:30
Jon
24b6bcfa47 feat(base): Enable user to customize log file location
ref: #744 #436 #752
2025-05-10 23:37:54 +09:30
Jon
b22baefa5a test(core): ensure ticket is un-solved for ticketcomment unit api render fields check
ref: #744 #726
2025-05-10 17:27:27 +09:30
Jon
a6e0f4e728 test(core): ensure slash command is called on ticket comment
ref: #744 #726
2025-05-10 17:23:37 +09:30
Jon
e366220e8b fix(core): ensure slash command is called on ticket description
ref: #744 #723
2025-05-10 16:42:23 +09:30
Jon
f71e304731 test(core): Unit ViewSet Test Suite for TicketCommentSolution
ref: #744 #728
2025-05-10 15:24:50 +09:30
Jon
55f58bb689 test(core): Unit ViewSet Test Suite for TicketCommentBase
ref: #744 #726
2025-05-10 15:24:27 +09:30
Jon
03b752759f test(core): Skip Related slash command checks until migrating tickets to new model
ref: #744 #746
2025-05-10 14:34:39 +09:30
Jon
85c6ed8483 test(core): Add ability to unit api field rendering test case for second api request if required
ref: #744 #726
2025-05-10 13:26:22 +09:30
Jon
a081c6a371 test(core): Partial Functional Model test cases (Slash Commands) for TicketCommentBase
ref: #744 #726
2025-05-09 21:05:54 +09:30
Jon
6c3122a3d8 test(core): Functional Model test cases (Slash Commands) for TicketBaseModel
ref: #744 #723
2025-05-09 21:04:31 +09:30
Jon
0c80b87606 test(core): Partial Slash Command re-write
ref: #744 #730
2025-05-09 21:03:44 +09:30
Jon
b6da539fcd fix(core): Spent slash command is valid for time spent
ref: #744
2025-05-09 21:02:55 +09:30
Jon
d4d99772b9 test(core): correct field so its valid for unit TicketCommentBase model
ref: #744 #726
2025-05-09 20:42:16 +09:30
Jon
4f8be43527 test(core): Unit API Fields Render for TicketCommentSolution model
ref: #744 #728
2025-05-09 20:41:26 +09:30
Jon
eb2282efac test(core): Unit API Fields Render for TicketCommentBase model
ref: #744 #726
2025-05-09 20:40:45 +09:30
Jon
85b5bf7b58 feat(core): Do validate the comment_type field for TicketCommentBase
ref: #744 #726
2025-05-09 20:08:06 +09:30
Jon
626a5ccb11 refactor(access): when fetching parent object, use the parent_model get function
ref: #744
2025-05-09 20:06:45 +09:30
Jon
806ffb2754 chore(python): upgrade django 5.1.8 -> 5.1.9
ref: #744
2025-05-09 16:25:33 +09:30
Jon
5900c13e08 docs(development): Add initial TicketCommentBase
ref: #744 #726
2025-05-09 16:21:42 +09:30
Jon
d399698eb1 test(core): Unit Model assert save and call are called for TicketBase
ref: #744 #723
2025-05-09 16:20:44 +09:30
Jon
457d329b0b fix(core): Correct logic for TicketCommentSolution
ref: #744 #728
2025-05-09 16:19:57 +09:30
Jon
45c428d30a fix(core): Correct logic for TicketCommentBase
ref: #744 #726
2025-05-09 16:19:33 +09:30
Jon
7ddb72239e chore(python): Add testing dep pytest-mock
ref: #744
2025-05-09 16:13:38 +09:30
Jon
580820ef44 test(core): Unit Model Checks for TicketCommentSolution
ref: #744 #728
2025-05-09 16:11:59 +09:30
Jon
d037150eb3 test(core): Unit Model Checks for TicketCommentBase
ref: #744 #726
2025-05-09 16:10:45 +09:30
Jon
35a102e4a9 Merge pull request #742 from nofusscomputing/test-asset-base 2025-05-08 15:44:35 +09:30
Jon
f7c75df9be docs(itam): Add IT Asset
ref: #742 closes #692
2025-05-08 15:30:11 +09:30
Jon
d0e18fe75a docs(development): Add Asset
ref: #742 closes #737
2025-05-08 15:29:59 +09:30
Jon
e30c08fd5b test(itam): test meta attribute itam_sub_model_type for ITAMBaseModel
ref: #742 #692
2025-05-08 15:10:38 +09:30
Jon
1058659174 test(itam): Dont use constants where variables should be used
ref: #742
2025-05-08 14:54:03 +09:30
Jon
b9ac588f87 test(itam): Remaining Unit Model test cases for AssetBase
ref: #742 #692
2025-05-08 14:54:03 +09:30
Jon
40b4bb0a3e test(accounting): Remaining Unit Model test cases for AssetBase
ref: #742 #737
2025-05-08 14:54:03 +09:30
Jon
9bd9652b3d fix(accounting): Ensure correct sub-model check is conducted within model type
ref: #742 #737
2025-05-08 14:47:56 +09:30
Jon
46c4fe9516 fix(itam): ensure RO field asset_type is set
ref: #742 #692
2025-05-08 14:25:47 +09:30
Jon
a71b5e6aba test(itam): Functional ViewSet Test Cases for ITAMAssetBase
ref: #742 #692
2025-05-08 13:42:24 +09:30
Jon
e0cbf1447f test(itam): Functional Serializer Test Cases for ITAMAssetBase
ref: #742 #692
2025-05-08 13:42:06 +09:30
Jon
b69e54f1e9 test(itam): Functional Permissions Test Cases for ITAMAssetBase
ref: #742 #692
2025-05-08 13:41:56 +09:30
Jon
ecb1c21179 test(itam): Functional Metadata Test Cases for ITAMAssetBase
ref: #742 #692
2025-05-08 13:41:49 +09:30
Jon
acff19ad58 test(itam): Functional History Test Cases for ITAMAssetBase
ref: #742 #692
2025-05-08 13:41:41 +09:30
Jon
f79f796a14 test(accounting): Functional ViewSet Test Cases for AssetBase
ref: #742 #737
2025-05-08 13:40:59 +09:30
Jon
a08a43e2bd test(accounting): Functional Serializer Test Cases for AssetBase
ref: #742 #737
2025-05-08 13:40:51 +09:30
Jon
8238e97a45 test(accounting): Functional Permissions Test Cases for AssetBase
ref: #742 #737
2025-05-08 13:40:44 +09:30
Jon
88202b57ee test(accounting): Functional Metadata Test Cases for AssetBase
ref: #742 #737
2025-05-08 13:40:36 +09:30
Jon
324fa39b56 test(accounting): History Test Cases for AssetBase
ref: #742 #737
2025-05-08 13:40:23 +09:30
Jon
233393e853 test: add missing merge of add_data for api permissions tests
ref: #742 #730
2025-05-08 13:38:56 +09:30
Jon
84afc3274a test: remove ticket only vars from api permissions tests
ref: #742 #730
2025-05-08 12:28:19 +09:30
Jon
a5bfc4977d Merge pull request #741 from nofusscomputing/base-asset-model 2025-05-06 04:20:14 +09:30
Jon
19205bbe8b feat(itam): Add Feature Flag 2025-00007 ITAMAssetBase
ref: #741 #692
2025-05-06 03:58:29 +09:30
Jon
f144e0b8ef feat(itam): Add endpoint for ITAMAssetBase
ref: #741 #692
2025-05-06 03:58:01 +09:30
Jon
8473d00148 feat: Model tag migration for Asset and IT Asset
ref: #741 #737 #692
2025-05-06 02:09:44 +09:30
Jon
c8391caf07 feat(itam): Model tag for ITAsset
ref: #741 #692
2025-05-06 02:08:17 +09:30
Jon
d19bb3a204 feat(accounting): Model tag for Asset
ref: #741 #737
2025-05-06 02:07:38 +09:30
Jon
ed71e935fc test(api): dont use constants for variable data
ref: #741
2025-05-06 01:43:38 +09:30
Jon
5ba243a1ea test: correct viewset tests
ref: #741
2025-05-06 00:48:29 +09:30
Jon
88a30650a5 test(itam): Unit Viewset checks for AssetBase Model
ref: #741 #692
2025-05-05 22:10:50 +09:30
Jon
420b223ca4 test(core): Add missing fields is_global checks for ticket base
ref: #741 #723
2025-05-05 22:08:42 +09:30
Jon
7168d519d1 test(api): Add submodel url resolution for metadata
ref: #741
2025-05-05 22:07:56 +09:30
Jon
4a09463f0a test(itam): Unit API Fields checks for ITAM AssetBase Model
ref: #741 #692
2025-05-05 20:45:30 +09:30
Jon
0a52029840 test(accounting): Unit API Fields checks for AssetBase Model
ref: #741 #737
2025-05-05 20:44:56 +09:30
Jon
6841b30a77 test: Support variables that were defined as properties.
ref: #741
2025-05-05 20:34:23 +09:30
Jon
dbaff89b8d test(api): Ensure that model notes is added to model create for api field tests
ref: #741
2025-05-05 17:49:17 +09:30
Jon
b7cd9ea75c fix(itim): Ensure that itam base model is always imported
ref: #741 #692
2025-05-05 17:33:30 +09:30
Jon
b54f3b7ab4 refactor(api): Limit url pk regex to ensure the value is a number
ref: #741
2025-05-05 17:22:08 +09:30
Jon
83d7c38c38 docs(accounting): added interim
ref: #741 #737
2025-05-05 16:58:44 +09:30
Jon
644dbc8159 feat(accounting): Add app label to kb articles for notes
ref: #741 #737
2025-05-05 16:38:41 +09:30
Jon
e566a5edf1 chore: add link to issue template for clarity of sub-model history variables
ref: #741
2025-05-05 16:35:03 +09:30
Jon
ef5c6b73af feat(accounting): Migrations for notes model for AssetBase
ref: #741 #737
2025-05-05 16:24:34 +09:30
Jon
3d7b133005 feat(accounting): Migrations for history model for AssetBase
ref: #741 #737
2025-05-05 16:24:24 +09:30
Jon
37a18d3c6e feat(accounting): Notes Viewset for AssetBase
ref: #741 #737
2025-05-05 16:24:00 +09:30
Jon
d6290471ba feat(accounting): Notes Serializer for AssetBase
ref: #741 #737
2025-05-05 16:23:33 +09:30
Jon
c13b360ca5 feat(accounting): Notes model for AssetBase
ref: #741 #737
2025-05-05 16:23:14 +09:30
Jon
e5cb0261ba feat(accounting): History model for AssetBase
ref: #741 #737
2025-05-05 16:22:48 +09:30
Jon
6c91cb008c chore(itam): just add this fucking thing cause vscode is a cunt and wont pick up the test correctly as its using an old value that does not exist even though the fucking code works directly from the console via pytest.
ref: #741 #692
2025-05-05 04:56:30 +09:30
Jon
9561301500 test(accounting): Unit Viewset checks for AssetBase Model
ref: #741 #737
2025-05-05 04:21:45 +09:30
Jon
349d67fa7b test(itam): Unit Model checks for ITAMAssetBase Model
ref: #741 #692
2025-05-05 02:46:41 +09:30
Jon
ce64664447 test(base): update Model base test suite for model_notes field
ref: #741 #737
2025-05-05 02:45:19 +09:30
Jon
370b8cd40f test(accounting): Unit Model checks for AssetBase Model
ref: #741 #737
2025-05-05 02:43:09 +09:30
Jon
efde919689 feat(itam): Serializer for ITAssetBase
ref: #741 #692
2025-05-05 01:00:14 +09:30
Jon
9a88b75654 feat(itam): Migrations for ITAssetBase
ref: #741 #692
2025-05-05 00:59:51 +09:30
Jon
0ceab03334 feat(itam): Add Model ITAssetBase
ref: #741 #692
2025-05-05 00:59:23 +09:30
Jon
c8cec06d85 feat(accounting): Viewset for Assets
ref: #741 #737
2025-05-05 00:58:08 +09:30
Jon
018cd7d245 feat(accounting): Serializer for model AssetBase
ref: #741 #737
2025-05-05 00:58:08 +09:30
Jon
471b5c08f6 feat(accounting): Migrations for model AssetBase
ref: #741 #737
2025-05-05 00:58:08 +09:30
Jon
b11a978962 feat(accounting): Add Model AssetBase
ref: #741 #737
2025-05-05 00:58:08 +09:30
Jon
95aba2b44b chore(accounting): Add permissions to permissions selector
ref: #741 #737
2025-05-05 00:58:08 +09:30
Jon
5d6d0e95ec chore(accounting): Add viewsets directory
ref: #741 #737
2025-05-05 00:58:08 +09:30
Jon
f0f75ecaa8 chore(accounting): Add models directory
ref: #740 #737
2025-05-04 21:16:29 +09:30
975a71bdd6 build: bump version 1.15.1 -> 1.16.0 2025-05-04 10:50:45 +00:00
Jon
f9e59835c1 Merge pull request #718 from nofusscomputing/feature-next-release 2025-05-04 20:01:58 +09:30
Jon
6ea66f7379 chore: squash migrations
ref: #718
2025-05-04 19:48:15 +09:30
Jon
fb768d1432 Merge pull request #734 from nofusscomputing/test-ticket-model 2025-05-04 03:30:01 +09:30
Jon
baa61155f7 test(core): Serializer Validation for ticket status change for TicketBase model
ref: #734 #723
2025-05-04 03:04:16 +09:30
Jon
c773fbc3a5 test(core): Prevent Closing / Solving of TicketBase Model if not ready
ref: #734 #723 closes #325
2025-05-04 03:04:09 +09:30
Jon
8c84ff7c52 test(itim): Incomplete Model Unit Tests for RequestTicket
ref: #734 #725
2025-05-03 22:56:09 +09:30
Jon
85d4cc8220 test(itim): Incomplete Model Unit Tests for SLMTicketBase
ref: #734 #727
2025-05-03 22:55:14 +09:30
Jon
70a2d502ef test(core): Incomplete Model Unit Tests for TicketBase
ref: #734 #723
2025-05-03 22:53:52 +09:30
Jon
393c8ce0ce refactor(test): rewrite model unit tests to use PyTest
ref: #734 #729 #735
2025-05-03 22:48:14 +09:30
Jon
92bc4aec43 fix(test): correct typo in attribute parameterized_
ref: #734 #729
2025-05-03 04:51:02 +09:30
Jon
4c46681d39 Merge pull request #733 from nofusscomputing/test-ticket-models 2025-05-01 01:42:47 +09:30
Jon
a898549469 test(itim): RequestTicket Updated, yet incomplete Test Suite for Serializer
ref: #733 #725
2025-05-01 01:23:40 +09:30
Jon
4e75faafb8 test(itim): SLMTicketBase Updated, yet incomplete Test Suite for Serializer
ref: #733 #727
2025-05-01 01:23:18 +09:30
Jon
1718a1cf14 test(core): TicketBase Updated, yet incomplete Test Suite for Serializer
ref: #733 #723
2025-05-01 01:22:42 +09:30
Jon
64757826da refactor(test): Update test parameterization
ref: #733 #730 #729
2025-05-01 01:22:22 +09:30
Jon
927776b9a7 chore(test): update pytest and required components
ref: #733
2025-05-01 00:40:06 +09:30
Jon
4ea5ddf122 test: Correct Test Suite for Serializer for models TicketBase, TicketRequest and TicketSLM
ref: #733 #723 #725 #727
2025-04-29 17:20:07 +09:30
Jon
847a5550e8 test(itim): RequestTicket Initial Test Suite for Serializer
ref: #733 #725
2025-04-29 16:14:40 +09:30
Jon
ea38665dab test(itim): SLMTicket Initial Test Suite for Serializer
ref: #733 #727
2025-04-29 16:13:59 +09:30
Jon
440adc09f4 test(core): TicketBase Initial Test Suite for Serializer
ref: #733 #723
2025-04-29 16:13:12 +09:30
Jon
9fcb1a8a44 test(core): SLMTicket Test Suite for ViewSet
ref: #733 #727
2025-04-29 15:59:45 +09:30
Jon
22808cf6c8 test(core): SLMTicket Test Suite for Metadata
ref: #733 #727
2025-04-29 15:59:37 +09:30
Jon
f978839ccc test(core): Request Test Suite for ViewSet
ref: #733 #725
2025-04-29 15:59:07 +09:30
Jon
54af17d75a test(core): Request Test Suite for Metadata
ref: #733 #725
2025-04-29 15:58:57 +09:30
Jon
c7eef1a97c test(core): TicketBase Test Suite for ViewSet
ref: #733 #723
2025-04-29 15:58:29 +09:30
Jon
59741e4197 test(core): TicketBase Test Suite for Metadata
ref: #733 #723
2025-04-29 15:58:05 +09:30
Jon
2518873e23 test(api): update test cases for SubModelViewSet Base Test Class
base class returns different results to inherited class.

ref: #733
2025-04-29 15:52:11 +09:30
Jon
029ab6bc06 refactor(api): SubModelViewSet must inherit from ModelViewSet as it's an extension
ref: #733 #564
2025-04-26 18:05:43 +09:30
Jon
a583db4b65 test(itim): RequestTicket ViewSet Test Suite
ref: #733 #723
2025-04-26 18:04:46 +09:30
Jon
9f8789f390 test(core): TicketBase ViewSet Test Suite
ref: #733 #723
2025-04-26 18:04:22 +09:30
Jon
c0b27c2886 test(api): Incomplete SubModelViewSet Test Cases
ref: #733 #564
2025-04-26 18:03:31 +09:30
Jon
cdacb19c30 test(api): SubModelViewSet Test Suite Setup
ref: #733 #564
2025-04-26 16:45:22 +09:30
Jon
471a1249c0 Merge pull request #732 from nofusscomputing/new-model-ticket-comment-base 2025-04-26 09:09:43 +09:30
Jon
53abf219a3 test: correct tests from Meta.sub_model_type changes
ref: #732
2025-04-26 08:40:29 +09:30
Jon
51c238734a chore(docs): Update issue template - New Model
ref: #732
2025-04-26 07:56:44 +09:30
Jon
efb0f52e8a chore(docs): correct linting errors
ref: #732
2025-04-26 07:48:02 +09:30
Jon
39dc8ad39f feat(core): Add ViewSet for Ticket Comments
ref: #732 #726
2025-04-26 07:38:55 +09:30
Jon
db47d0e160 feat(project_management): Depreciate Project Task Ticket Endpoint
ref: #732 #564
2025-04-26 07:38:10 +09:30
Jon
da13343b27 feat(itim): Depreciate Problem Ticket Endpoint
ref: #732 #564
2025-04-26 07:37:24 +09:30
Jon
8100030870 feat(itim): Depreciate Incident Ticket Endpoint
ref: #732 #564
2025-04-26 07:37:04 +09:30
Jon
5097db6f94 feat(itim): Depreciate Change Ticket Endpoint
ref: #732 #564
2025-04-26 07:36:01 +09:30
Jon
b729842a1c feat(assistance): Depreciate Ticket Comment
ref: #732 #564 #726
2025-04-26 07:34:55 +09:30
Jon
bc1d5ffc03 feat(assistance): Depreciate Request Ticket Endpoint
ref: #732 #564 #725
2025-04-26 07:33:45 +09:30
Jon
ae1f600147 feat(core): Add routes for Ticket Comments
ref: #732 #726
2025-04-26 07:28:25 +09:30
Jon
edc3f9fd3c feat(core): update ticket serializer to use new comment base url
ref: #732 #723
2025-04-26 07:11:12 +09:30
Jon
8dc922a8bb feat(core): Add permissions import, purge and triage to model TicketCommentSolution
ref: #732 #728
2025-04-26 07:10:18 +09:30
Jon
9461cdaf9d feat(core): Add permissions import, purge and triage to model TicketCommentBase
ref: #732 #726
2025-04-26 07:09:33 +09:30
Jon
a923a21660 feat(core): Filter ticket_comment_model routes to those defined in Meta.sub_model_type
ref: #732 #726
2025-04-26 06:56:31 +09:30
Jon
dcd60a5152 feat(core): Filter ticket_model routes to those defined in Meta.sub_model_type
ref: #732 #723
2025-04-26 06:55:54 +09:30
Jon
50882a4732 feat(access): Filter entity_model routes to thos defined in Meta.sub_model_type
ref: #732 #704
2025-04-26 06:53:58 +09:30
Jon
9cd59e8446 feat(core): Serializer for TicketCommentBase
ref: #732 #726
2025-04-26 06:34:58 +09:30
Jon
9905edc28c feat(core): Serializer for TicketCommentSolution
ref: #732 #728
2025-04-26 06:34:12 +09:30
Jon
98479f39e6 feat(core): Ticket Comment Get URL functions
ref: #732 #726
2025-04-26 06:28:18 +09:30
Jon
bdfd3f42c4 feat(core): Ticket Comment Validation for comment_type
must be derived from `Meta.sub_model_type`

ref: #732 #726
2025-04-26 06:27:36 +09:30
Jon
ca022e0697 fix(core): Ticktet comment can have empty body
i.e. time tracking

ref: #732 #726
2025-04-26 06:26:31 +09:30
Jon
1719b6c417 feat(core): Update choices fields for TicketCommentBase model
ref: #732 #726
2025-04-26 06:25:31 +09:30
Jon
bb0f9b56e6 feat(core): init for model TicketCommentSolution
ref: #732 #728
2025-04-26 06:24:00 +09:30
Jon
107a3aa17f feat(core): Migrations for choice fields for TicketBase model
ref: #732 #723
2025-04-26 06:23:33 +09:30
Jon
1c9be27e1f feat(core): Migrations for model TicketCommentSolution
ref: #732 #728
2025-04-26 06:23:07 +09:30
Jon
dc25d483fc feat(core): Update choice fields for TicketBase model
ref: #732 #723
2025-04-26 06:22:25 +09:30
Jon
33362a28d5 feat(core): New model TicketCommentSolution
ref: #732 #728
2025-04-26 06:21:04 +09:30
Jon
b476bd458e fix(core): If model does not save history, dont attempt to cache before
ref: #732
2025-04-26 06:19:35 +09:30
Jon
c152dd13f1 feat(api): when fetching related_object, default to base_model for SubModelViewSet
ref: #732 #726
2025-04-26 06:13:39 +09:30
Jon
677cc55e04 test: correct serializer imports from recent file renames
ref: #732 #726
2025-04-26 06:08:37 +09:30
Jon
b6146b7d14 feat: Add field Meta.sub_model_type to sub-models
ref: #732 #564
2025-04-26 06:00:34 +09:30
Jon
dbc849d3f1 chore(core): rename viewsets/ticket_comment.py -> viewsets/ticket_comment_depreciated.py
ref: #732 #726
2025-04-26 05:49:19 +09:30
Jon
8d1098ec9e fix(itam): provide return_url as part of software version meta
ref: #718
2025-04-25 17:01:52 +09:30
Jon
63923e8a9b Merge pull request #731 from nofusscomputing/refactor-test-api 2025-04-24 13:22:54 +09:30
Jon
7261f60e3b chore(docs): correct linting error
ref: #731
2025-04-24 13:09:00 +09:30
Jon
fef14b8be8 feat(core): New interim model TicketCommentSolution
ref: #731 #728
2025-04-24 12:44:25 +09:30
Jon
17e4cb4148 docs(development): update testing docs
ref: #731 #730
2025-04-24 12:42:00 +09:30
Jon
99211b7505 test: Fixture for creating model with random data
ref: #731 #730
2025-04-24 12:41:16 +09:30
Jon
c4e81ffbed test(itim): API Field checks for TicketSLMBase
ref: #731 #727
2025-04-24 12:36:45 +09:30
Jon
683b134053 test(itim): API Field checks for TicketRequest
ref: #731 #725
2025-04-24 12:36:09 +09:30
Jon
2a5c86d961 fix(itim): correct ticket_slm serializer
ref: #731 #727
2025-04-23 18:58:13 +09:30
Jon
ca28a67194 fix(itim): correct ticket_request serializer
ref: #731 #725
2025-04-23 18:57:41 +09:30
Jon
d904ce7100 chore: migrate app/.coveragerc to -> pyproject.toml
ref: #731
2025-04-23 18:33:35 +09:30
Jon
daf30de835 chore: correct tests so they function and cleanup correctly
ref: #731
2025-04-23 18:33:10 +09:30
Jon
7c9819efd1 test(core): API fields Tests for TicketBase
ref: #731 #723
2025-04-22 23:57:19 +09:30
Jon
42bea64ff5 test(core): API fields Unit Test Suite
ref: #731 #730
2025-04-22 22:25:23 +09:30
Jon
537f167b9e test(core): Correct model notes test suite
ref: #731
2025-04-22 22:22:58 +09:30
Jon
0a630cf51a test(core): API Permission Test Cases for ticket_base model
ref: #731 #723
2025-04-22 22:21:42 +09:30
Jon
29e07e6cac feat(core): add ticket routes
ref: #731 #723
2025-04-22 22:18:40 +09:30
Jon
0f21f8daf0 test(api): add API Permission Test Cases
ref: #731 #730
2025-04-22 22:17:32 +09:30
Jon
6289fad8ad chore: migrate from pytest.ini -> pyproject.toml
ref: #731 #730
2025-04-22 21:44:05 +09:30
Jon
705b245514 chore: add pytest to pyproject.toml
ref: #731
2025-04-20 01:21:10 +09:30
Jon
9e1a426240 Merge pull request #724 from nofusscomputing/refactor-core-model-ticket 2025-04-19 20:38:03 +09:30
Jon
c80a5b5232 docs(development): initial ticket docs
ref: #724 #564 #723
2025-04-16 21:10:59 +09:30
Jon
eddd2534ea fix(api): SubModelViewSet.related_objects must be the same class as the base model
ref: #724 #723 #725
2025-04-16 21:07:14 +09:30
Jon
9dc1ae2d05 feat(itim): serializer for SLMTicketBase
ref: #724 #727
2025-04-16 21:05:32 +09:30
Jon
4b8cb9633e feat(itim): Serializer for RequestTicket
ref: #724 #725
2025-04-16 21:05:15 +09:30
Jon
8016c4b4c8 feat(itim): migrations for RequestTicket
ref: #724 #725
2025-04-16 21:05:00 +09:30
Jon
3efadfa75f feat(itim): New Model RequestTicket
ref: #724 #725
2025-04-16 21:04:45 +09:30
Jon
66bbace8be feat(itim): migration for SLMTicketBase
ref: #724 #727
2025-04-16 21:03:54 +09:30
Jon
7ff638ed58 feat(itim): New Model SLMTicketBase
ref: #724 #727
2025-04-16 21:02:10 +09:30
Jon
ddebf50e88 feat(core): migrations for TicketCommentBase
ref: #724 #726
2025-04-16 21:01:22 +09:30
Jon
64144ea76f feat(core): New Model TicketCommentBase
ref: #724 #726
2025-04-16 21:01:09 +09:30
Jon
633df58964 feat(core): viewset for TicketBase
ref: #724 #723
2025-04-16 21:00:15 +09:30
Jon
f280f408ee feat(core): serializer for TicketBase
ref: #724 #723
2025-04-16 21:00:01 +09:30
Jon
d1715f4457 feat(core): migrations for TicketBase
ref: #724 #723
2025-04-16 20:59:50 +09:30
Jon
e80647ffd1 feat(core): New Model TicketBase
ref: #724 #723
2025-04-16 20:59:36 +09:30
Jon
1a5f328a24 fix(access): Ensure related model is a sub-model
ref: #724 #723
2025-04-16 20:54:30 +09:30
Jon
4872c6d1c8 refactor(core): rename ticket model filename in preparation for base ticket model
ref: #724 #723
2025-04-16 00:38:15 +09:30
Jon
a695b4563d refactor(access): migrate sub-model viewset logic to common
ref: #724 #723
2025-04-15 20:13:39 +09:30
Jon
b89993f89d Merge pull request #722 from nofusscomputing/model-employee 2025-04-15 17:27:30 +09:30
Jon
8f69e6ec79 test(access): Correct history link test cases
ref: #722 #684
2025-04-15 17:09:38 +09:30
Jon
8984aeebb8 test(project_management): Add test cases for api field render for model fields estimation_project and duration_project
ref: #722 closes #312
2025-04-15 16:47:02 +09:30
Jon
a0d750b1f5 feat(project_management): add estimation field to project api fields
ref: #722 closes #312
2025-04-15 16:44:23 +09:30
Jon
75618de977 refactor(project_management): add duration field to project api fields
ref: #722 #312
2025-04-15 16:41:07 +09:30
Jon
2e1358da83 fix(human_resources): Correct history link generation and add docs
ref: #722 #707 fixes #710
2025-04-15 16:20:12 +09:30
Jon
8ecb1fe01a fix(human_resources): Correct history link generation
ref: #722 #684
2025-04-15 16:17:33 +09:30
Jon
0ff8f8650b refactor(human_resources): Move employee details to its own section
ref: #722 #684
2025-04-15 15:46:06 +09:30
Jon
529a7fa385 chore(human_resources): add employee to feature flag 2025-00005
ref: #722 #684 #92
2025-04-15 15:45:43 +09:30
Jon
6258fb2a33 chore(release_notes): add employee note to release notes
ref: #722 closes #684
2025-04-15 15:26:45 +09:30
Jon
0a2ede62b7 test(human_resources): History Serializer and ViewSet Functional test suites for employee
ref: #722 #684
2025-04-15 15:23:35 +09:30
Jon
559e65f990 test(human_resources): APIv2, History, Model and ViewSet Unit test suites for employee
ref: #722 #684
2025-04-15 15:22:56 +09:30
Jon
e71f7a6942 feat(human_resources): nav menu entries for Employee
ref: #722 #684
2025-04-15 15:22:09 +09:30
Jon
f12a2e37f3 feat(human_resources): Serializer for Employee
ref: #722 #684
2025-04-15 15:21:24 +09:30
Jon
c1614b1182 feat(human_resources): Migration for Employee
ref: #722 #684
2025-04-15 15:21:08 +09:30
Jon
6b4bd4db35 feat(human_resources): New model Employee
ref: #722 #684
2025-04-15 15:20:48 +09:30
Jon
163d7bcd6c feat(devops): add missing api index menu entry for devops
ref: #722
2025-04-15 15:09:34 +09:30
Jon
d131d2499b feat(access): add missing nav menu entries for entities
ref: #722
2025-04-15 15:08:38 +09:30
Jon
4e03c2bf31 feat(human_resources): add module to perms selector
ref: #722
2025-04-15 15:07:50 +09:30
Jon
9e05410f28 Merge pull request #719 from nofusscomputing/test-tenancy-model 2025-04-13 16:50:30 +09:30
Jon
9c88c23bb3 test: Migrate models to use refactored model tests
ref: #719 closes #708
2025-04-13 16:35:48 +09:30
Jon
e4588771e8 test: Consolidate All model tests to remove duplicates and to simplify
ref: #719 #708
2025-04-13 13:48:54 +09:30
Jon
507c42faea chore: Add branch coverage to makefile test
ref: #719 #708
2025-04-12 19:04:11 +09:30
Jon
9302627c6f fix(access): add missing attribute to Tenancy object
ref: #718
2025-04-12 19:01:44 +09:30
667 changed files with 28439 additions and 6905 deletions

View File

@ -17,5 +17,5 @@ commitizen:
prerelease_offset: 1
tag_format: $version
update_changelog_on_bump: false
version: 1.15.1
version: 1.18.0
version_scheme: semver

View File

@ -29,10 +29,16 @@ Describe in detail the following:
<!-- Don't remove tasks strike them out. use `~~` before and after the item. i.e. `- ~~[ ] Model Created~~` note: don't include the list dash-->
- [ ] 🆕 Model Created
- [ ] 🆕 [Model Created](https://nofusscomputing.com/projects/centurion_erp/development/models/)
- [ ] 🛠️ Migrations added
- [ ] ♻️ Serializer Created
- [ ] 🔄 [ViewSet Created](https://nofusscomputing.com/projects/centurion_erp/development/views/)
- [ ] 🔗 URL Route Added
- [ ] 🏷️ Model tag added to `app/core/lib/slash_commands/linked_model.CommandLinkedModel.get_model()` function
- [ ] 📘 Tag updated in the [docs](https://nofusscomputing.com/projects/centurion_erp/user/core/markdown/#model-reference)
@ -44,7 +50,13 @@ Describe in detail the following:
> - Two words are not to contain a space char, `\s`. It is to be replaced with an underscore `_`
> - As much as practical, keep the tag as close to the model name as possible
- [ ] 📝 New [History model](https://nofusscomputing.com/projects/centurion_erp/development/core/model_history/) created
- [ ] 📝 New [History model](https://nofusscomputing.com/projects/centurion_erp/development/core/model_history/) created
- Sub-Models **_ONLY_**
- [ ] Model class variable [`history_app_label`](https://nofusscomputing.com/projects/centurion_erp/development/models/#history) set to correct application label
- [ ] Model class variable [`history_model_name`](https://nofusscomputing.com/projects/centurion_erp/development/models/#history) set to correct model label
- [ ] 📓 New [Notes model](https://nofusscomputing.com/projects/centurion_erp/development/core/model_notes/) created
- [ ] 🆕 Model Created
@ -66,16 +78,18 @@ Describe in detail the following:
### 🧪 Tests
- [ ] Unit Test Model
- [ ] Unit Test Tenancy Object
- [ ] Unit Test Serializer
- [ ] Unit Test ViewSet
- [ ] Function Test ViewSet
- [ ] Function Test API Metadata
- [ ] Function Test API Permissions
- [ ] Function Test API Render (fields)
- [ ] Function Test History Entries
- [ ] Function Test History API Render (fields)
- Unit Tests
- [ ] API Render (fields)
- [ ] [Model](https://nofusscomputing.com/projects/centurion_erp/development/models/#tests)
- [ ] ViewSet
- Function Test
- [ ] History API Render (fields)
- [ ] History Entries
- [ ] API Metadata
- [ ] API Permissions
- [ ] Model
- [ ] Serializer
- [ ] ViewSet
## ✅ Requirements
@ -84,6 +98,24 @@ A Requirement is a must have. In addition will also be tested.
- [ ] Must have a [model_tag](https://nofusscomputing.com/projects/centurion_erp/user/core/markdown/#model-reference)
<!--
When detailing requirements the following must be taken into account:
- what the user should be able to do
- what the user should not be able to do
- what should occur when a user performs an action
-->
- Functional Requirements
- Non-Functional Requirements
---
<!-- Add additional requirement here and as a check box list -->

7
.gitignore vendored
View File

@ -1,9 +1,10 @@
venv/**
*/static/**
__pycache__
**.sqlite3
**.sqlite*
**.sqlite
**.coverage
.coverage*
artifacts/
**.tmp.*
volumes/
@ -15,4 +16,8 @@ node_modules/
package-lock.json
package.json
**.junit.xml
**.JUnit.xml
feature_flags.json
coverage_*.json
*-coverage.xml
log/

35
.vscode/launch.json vendored
View File

@ -4,7 +4,6 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Centurion",
"type": "debugpy",
@ -18,7 +17,6 @@
"program": "${workspaceFolder}/app/manage.py"
},
{
"name": "Debug: Gunicorn",
"type": "debugpy",
"request": "launch",
@ -32,7 +30,6 @@
"--bind",
"0.0.0.0:8002",
"app.wsgi:application",
],
"django": true,
"autoStartBrowser": false,
@ -53,7 +50,6 @@
"autoStartBrowser": false,
"program": "${workspaceFolder}/app/manage.py"
},
{
"name": "Migrate",
"type": "debugpy",
@ -64,7 +60,6 @@
"django": true,
"autoStartBrowser": false,
"program": "${workspaceFolder}/app/manage.py"
},
{
"name": "Debug: Celery",
@ -82,6 +77,34 @@
"debug-itsm@%h"
],
"cwd": "${workspaceFolder}/app"
}
},
{
"name": "Debug pytest (collect)",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"args": [
"--override-ini", "addopts=",
"--collect-only",
"app",
],
"console": "integratedTerminal",
"justMyCode": false
},
{
"name": "Python Debugger: Local Attach",
"type": "debugpy",
"request": "attach",
"connect": {
"host": "localhost",
"port": 5678
},
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "."
}
]
},
]
}

14
.vscode/settings.json vendored
View File

@ -5,19 +5,23 @@
"!python"
],
"python.testing.pytestArgs": [
// "-v",
// "--cov",
// "--cov-report xml",
"-s",
"--override-ini", "addopts=",
"app",
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"testing.coverageToolbarEnabled": true,
"cSpell.words": [
"ITSM"
],
"cSpell.language": "en-AU",
"jest.enable": false,
"pylint.enabled": true,
"testing.showCoverageInExplorer": true,
"testing.coverageToolbarEnabled": true,
"testing.coverageBarThresholds": {
"red": 0,
"yellow": 60,
"green": 90
},
"telemetry.feedback.enabled": false,
}

View File

@ -1,3 +1,305 @@
## 1.18.0 (2025-07-03)
### feat
- **python**: upgrade django 5.1.9 -> 5.1.10
### Fixes
- **itim**: Correct config that is in the incorrect format
## 1.17.1 (2025-06-02)
### Fixes
- **base**: Add python metrics to prometheus exporter
## 1.17.0 (2025-05-16)
### feat
- **access**: model access.Company feature flag `2025-00008`
- **access**: URL route for model access.Company
- **access**: Migration for model access.Company
- **access**: Serializer for model access.Company
- **access**: New model access.Company
- **access**: Organization -> Tenant Permission Migration
- **docker**: Serve a robots.txt file for NO indexing
- **access**: Organization -> Tenant Permission Migration
- **base**: Add var `AUTH_USER_MODEL` to settings
- **core**: Add Action comments on ticket change
- **core**: Remove add, change and delete permissions for model TicketCommentAction from permission selector
- **core**: Serializer for model TicketCommentAction
- **core**: Migrations for model TicketCommentAction
- **core**: New model TicketCommentAction
- **core**: Setup serializer to meet requirements
- **core**: Setup model to meet requirements
- **api**: Add exception logging to ViewSetCommon
- **python**: Upgrade DRF Spectacular 0.27.2 -> 0.28.0
- **python**: Upgrade DRF 3.15.2 -> 3.16.0
- **core**: When processing slash command duration, cater for new ticket models
- **api**: Add Logging function to Common ViewSet
- **access**: Add Logging function to Tenancy model
- **base**: Enable user to customize log file location
- **core**: Do validate the comment_type field for TicketCommentBase
- **itam**: Add Feature Flag `2025-00007` ITAMAssetBase
- **itam**: Add endpoint for ITAMAssetBase
- Model tag migration for Asset and IT Asset
- **itam**: Model tag for ITAsset
- **accounting**: Model tag for Asset
- **accounting**: Add app label to kb articles for notes
- **accounting**: Migrations for notes model for AssetBase
- **accounting**: Migrations for history model for AssetBase
- **accounting**: Notes Viewset for AssetBase
- **accounting**: Notes Serializer for AssetBase
- **accounting**: Notes model for AssetBase
- **accounting**: History model for AssetBase
- **itam**: Serializer for ITAssetBase
- **itam**: Migrations for ITAssetBase
- **itam**: Add Model ITAssetBase
- **accounting**: Viewset for Assets
- **accounting**: Serializer for model AssetBase
- **accounting**: Migrations for model AssetBase
- **accounting**: Add Model AssetBase
### Fixes
- **api**: Dont try to access attribute if not exist in common viewset
- **api**: Dont try to access attribute if not exist in common viewset
- **api**: Correct ViewSet Sub-Model lookup
- **core**: Only take action on ticket comment if view exists
- **api**: Ensure multi-nested searching for sub-models works
- **core**: ensure slash command is called on ticket description
- **core**: Spent slash command is valid for time spent
- **core**: Correct logic for TicketCommentSolution
- **core**: Correct logic for TicketCommentBase
- **accounting**: Ensure correct sub-model check is conducted within model type
- **itam**: ensure RO field asset_type is set
- **itim**: Ensure that itam base model is always imported
### Refactoring
- **human_resources**: Update Functional ViewSet to use PyTest for Employee Model
- **Access**: Update Functional ViewSet to use PyTest for Person Model
- **Access**: Update Functional ViewSet to use PyTest for Entity Model
- **Access**: Update Functional ViewSet to use PyTest for Contact Model
- **Access**: Update Functional Permission to use PyTest for Person Model
- **Access**: Update Functional Permission to use PyTest for Entity Model
- **Access**: Update Functional Permission to use PyTest for Contact Model
- **Access**: Update Functional Serializer to use PyTest for Contact Model
- **Access**: Update Functional Serializer to use PyTest for Entity Model
- **Access**: Update Functional Serializer to use PyTest for Person Model
- **human_resources**: Update Functional Serializer to use PyTest for Employee Model
- **human_resources**: Update Functional Permissions to use PyTest for Employee Model
- **human_resources**: Update Functional Metadata to use PyTest for Employee Model
- **access**: Update Functional Metadata to use PyTest for Person Model
- **access**: Update Functional Metadata to use PyTest for Entity Model
- **access**: Update Functional Metadata to use PyTest for Contact Model
- **access**: Update Model Entity to use PyTest for Model Test Suite
- **access**: Update Model Contact to use PyTest for Model Test Suite
- **access**: Update Model Person to use PyTest for Model Test Suite
- **human_resources**: Update Model Employee to use PyTest for Model Test Suite
- **human_resources**: Update Model Employee to use PyTest API Fields Render
- **access**: Update Model Person to use PyTest API Fields Render
- **access**: Update Model Contact to use PyTest API Fields Render
- **access**: Update Model Entity to use PyTest API Fields Render
- **access**: Rename model Organization -> Tenant
- **settings**: Update all references to `User` to use `get_user_model()`
- **project_management**: Update all references to `User` to use `get_user_model()`
- **itam**: Update all references to `User` to use `get_user_model()`
- **devops**: Update all references to `User` to use `get_user_model()`
- **core**: Update all references to `User` to use `get_user_model()`
- **config_management**: Update all references to `User` to use `get_user_model()`
- **assistance**: Update all references to `User` to use `get_user_model()`
- **app**: Update all references to `User` to use `get_user_model()`
- **api**: Update all references to `User` to use `get_user_model()`
- **accounting**: Update all references to `User` to use `get_user_model()`
- **access**: Update all references to `User` to use `get_user_model()`
- **access**: when fetching parent object, use the parent_model get function
- **api**: Limit url pk regex to ensure the value is a number
### Tests
- **access**: Functional ViewSet Test Suite Company model
- **access**: Functional Serializer Test Suite Company model
- **access**: Functional Permissions Test Suite Company model
- **access**: Functional MetaData Test Suite Company model
- **access**: ViewSet Test Suite Company model
- **access**: API field render Test Suite Company model
- **access**: Model Test Suite Company model
- **core**: Unit viewset Test Cases for TicketCommentAction model
- **core**: Unit model Test Cases for TicketCommentAction model
- **core**: Unit API Render Test Cases for TicketCommentAction model
- **core**: Interim Functional model Test Case TicketCommentAction
- **core**: Ensure that a ticket milestone comes from the same assigned project
- **core**: SKIP Tests TicketBase Description Slash command Checks
- **core**: TicketBase Description Slash command Checks
- **core**: TicketBase Remaining Serializer Chacks
- **core**: Partial functional Model Test Suite covering some slash commande for TicketCommentSolution
- **core**: ensure ticket is un-solved for ticketcomment unit api render fields check
- **core**: ensure slash command is called on ticket comment
- **core**: Unit ViewSet Test Suite for TicketCommentSolution
- **core**: Unit ViewSet Test Suite for TicketCommentBase
- **core**: Skip Related slash command checks until migrating tickets to new model
- **core**: Add ability to unit api field rendering test case for second api request if required
- **core**: Partial Functional Model test cases (Slash Commands) for TicketCommentBase
- **core**: Functional Model test cases (Slash Commands) for TicketBaseModel
- **core**: Partial Slash Command re-write
- **core**: correct field so its valid for unit TicketCommentBase model
- **core**: Unit API Fields Render for TicketCommentSolution model
- **core**: Unit API Fields Render for TicketCommentBase model
- **core**: Unit Model assert save and call are called for TicketBase
- **core**: Unit Model Checks for TicketCommentSolution
- **core**: Unit Model Checks for TicketCommentBase
- **itam**: test meta attribute itam_sub_model_type for ITAMBaseModel
- **itam**: Dont use constants where variables should be used
- **itam**: Remaining Unit Model test cases for AssetBase
- **accounting**: Remaining Unit Model test cases for AssetBase
- **itam**: Functional ViewSet Test Cases for ITAMAssetBase
- **itam**: Functional Serializer Test Cases for ITAMAssetBase
- **itam**: Functional Permissions Test Cases for ITAMAssetBase
- **itam**: Functional Metadata Test Cases for ITAMAssetBase
- **itam**: Functional History Test Cases for ITAMAssetBase
- **accounting**: Functional ViewSet Test Cases for AssetBase
- **accounting**: Functional Serializer Test Cases for AssetBase
- **accounting**: Functional Permissions Test Cases for AssetBase
- **accounting**: Functional Metadata Test Cases for AssetBase
- **accounting**: History Test Cases for AssetBase
- add missing merge of add_data for api permissions tests
- remove ticket only vars from api permissions tests
- **api**: dont use constants for variable data
- correct viewset tests
- **itam**: Unit Viewset checks for AssetBase Model
- **core**: Add missing fields is_global checks for ticket base
- **api**: Add submodel url resolution for metadata
- **itam**: Unit API Fields checks for ITAM AssetBase Model
- **accounting**: Unit API Fields checks for AssetBase Model
- Support variables that were defined as properties.
- **api**: Ensure that model notes is added to model create for api field tests
- **accounting**: Unit Viewset checks for AssetBase Model
- **itam**: Unit Model checks for ITAMAssetBase Model
- **base**: update Model base test suite for model_notes field
- **accounting**: Unit Model checks for AssetBase Model
## 1.16.0 (2025-05-04)
### feat
- **core**: Add ViewSet for Ticket Comments
- **project_management**: Depreciate Project Task Ticket Endpoint
- **itim**: Depreciate Problem Ticket Endpoint
- **itim**: Depreciate Incident Ticket Endpoint
- **itim**: Depreciate Change Ticket Endpoint
- **assistance**: Depreciate Ticket Comment
- **assistance**: Depreciate Request Ticket Endpoint
- **core**: Add routes for Ticket Comments
- **core**: update ticket serializer to use new comment base url
- **core**: Add permissions `import`, `purge` and `triage` to model TicketCommentSolution
- **core**: Add permissions `import`, `purge` and `triage` to model TicketCommentBase
- **core**: Filter ticket_comment_model routes to those defined in `Meta.sub_model_type`
- **core**: Filter ticket_model routes to those defined in `Meta.sub_model_type`
- **access**: Filter entity_model routes to thos defined in `Meta.sub_model_type`
- **core**: Serializer for TicketCommentBase
- **core**: Serializer for TicketCommentSolution
- **core**: Ticket Comment Get URL functions
- **core**: Ticket Comment Validation for comment_type
- **core**: Update choices fields for TicketCommentBase model
- **core**: init for model TicketCommentSolution
- **core**: Migrations for choice fields for TicketBase model
- **core**: Migrations for model TicketCommentSolution
- **core**: Update choice fields for TicketBase model
- **core**: New model TicketCommentSolution
- **api**: when fetching related_object, default to base_model for SubModelViewSet
- Add field `Meta.sub_model_type` to sub-models
- **core**: New interim model TicketCommentSolution
- **core**: add ticket routes
- **itim**: serializer for SLMTicketBase
- **itim**: Serializer for RequestTicket
- **itim**: migrations for RequestTicket
- **itim**: New Model RequestTicket
- **itim**: migration for SLMTicketBase
- **itim**: New Model SLMTicketBase
- **core**: migrations for TicketCommentBase
- **core**: New Model TicketCommentBase
- **core**: viewset for TicketBase
- **core**: serializer for TicketBase
- **core**: migrations for TicketBase
- **core**: New Model TicketBase
- **project_management**: add estimation field to project api fields
- **human_resources**: nav menu entries for Employee
- **human_resources**: Serializer for Employee
- **human_resources**: Migration for Employee
- **human_resources**: New model Employee
- **devops**: add missing api index menu entry for devops
- **access**: add missing nav menu entries for entities
- **human_resources**: add module to perms selector
### Fixes
- **test**: correct typo in attribute parameterized_
- **core**: Ticktet comment can have empty body
- **core**: If model does not save history, dont attempt to cache before
- **itam**: provide return_url as part of software version meta
- **itim**: correct ticket_slm serializer
- **itim**: correct ticket_request serializer
- **api**: SubModelViewSet.related_objects must be the same class as the base model
- **access**: Ensure related model is a sub-model
- **human_resources**: Correct history link generation and add docs
- **human_resources**: Correct history link generation
- **access**: add missing attribute to Tenancy object
### Refactoring
- **test**: rewrite model unit tests to use PyTest
- **test**: Update test parameterization
- **api**: SubModelViewSet must inherit from ModelViewSet as it's an extension
- **core**: rename ticket model filename in preparation for base ticket model
- **access**: migrate sub-model viewset logic to common
- **project_management**: add duration field to project api fields
- **human_resources**: Move employee details to its own section
### Tests
- **core**: Serializer Validation for ticket status change for TicketBase model
- **core**: Prevent Closing / Solving of TicketBase Model if not ready
- **itim**: Incomplete Model Unit Tests for RequestTicket
- **itim**: Incomplete Model Unit Tests for SLMTicketBase
- **core**: Incomplete Model Unit Tests for TicketBase
- **itim**: RequestTicket Updated, yet incomplete Test Suite for Serializer
- **itim**: SLMTicketBase Updated, yet incomplete Test Suite for Serializer
- **core**: TicketBase Updated, yet incomplete Test Suite for Serializer
- Correct Test Suite for Serializer for models TicketBase, TicketRequest and TicketSLM
- **itim**: RequestTicket Initial Test Suite for Serializer
- **itim**: SLMTicket Initial Test Suite for Serializer
- **core**: TicketBase Initial Test Suite for Serializer
- **core**: SLMTicket Test Suite for ViewSet
- **core**: SLMTicket Test Suite for Metadata
- **core**: Request Test Suite for ViewSet
- **core**: Request Test Suite for Metadata
- **core**: TicketBase Test Suite for ViewSet
- **core**: TicketBase Test Suite for Metadata
- **api**: update test cases for SubModelViewSet Base Test Class
- **itim**: RequestTicket ViewSet Test Suite
- **core**: TicketBase ViewSet Test Suite
- **api**: Incomplete SubModelViewSet Test Cases
- **api**: SubModelViewSet Test Suite Setup
- correct tests from Meta.sub_model_type changes
- correct serializer imports from recent file renames
- Fixture for creating model with random data
- **itim**: API Field checks for TicketSLMBase
- **itim**: API Field checks for TicketRequest
- **core**: API fields Tests for TicketBase
- **core**: API fields Unit Test Suite
- **core**: Correct model notes test suite
- **core**: API Permission Test Cases for ticket_base model
- **api**: add API Permission Test Cases
- **access**: Correct history link test cases
- **project_management**: Add test cases for api field render for model fields `estimation_project` and `duration_project`
- **human_resources**: History Serializer and ViewSet Functional test suites for employee
- **human_resources**: APIv2, History, Model and ViewSet Unit test suites for employee
- Migrate models to use refactored model tests
- Consolidate All model tests to remove duplicates and to simplify
## 1.15.1 (2025-04-10)
### Fixes

View File

@ -1,3 +1,49 @@
## Version 1.17.0
- Added setting for log files.
Enables user to specify a default path for centurion's logging. Add the following to your settings file `/etc/itsm/settings.py`
``` py
LOG_FILES = {
"centurion": "/var/log/centurion.log", # Normal Centurion Operations
"weblog": "/var/log/weblog.log", # All web requests made to Centurion
"rest_api": "/var/log/rest_api.log", # Rest API
"catch_all":"/var/log/catch-all.log" # A catch all log. Note: does not log anything that has already been logged.
}
```
With this new setting, the previous setting `LOGGING` will no longer function.
- Renamed `Organization` model to `Tenant` so as to reflect what is actually is.
- `robots.txt` file now being served from the API container at path `/robots.txt` with `User-agent: *` and `Disallow: /`
## Version 1.16.0
- Employees model added behind feature flag `2025-00002` and will remain behind this flag until production ready.
- Ticket and Ticket Comment added behind feature flag `2025-00006` and will remain behind this flag until production ready.
- In preparation of the [Ticket and Ticket Comment model re-write](https://github.com/nofusscomputing/centurion_erp/issues/564)
- Depreciated Change Ticket
- Depreciated Ticket Comment Endpoint
- Depreciated Request Ticket
- Depreciated Incident Ticket
- Depreciated Problem Ticket
- Depreciated Project Task Ticket
These endpoints still work and will remain so until the new Ticket and Ticket Comment Models are production ready.
## Version 1.15.0
- Entities model added behind feature flag `2025-00002` and will remain behind this flag until production ready.

View File

@ -1,17 +0,0 @@
[run]
source = .
omit =
*migrations/*
*tests/*/*
[report]
omit =
*/tests/*/*
*/migrations/*
*apps.py
*manage.py
*__init__.py
*asgi*
*wsgi*
*admin.py
*urls.py

View File

@ -1,12 +1,15 @@
import django
from django.contrib import admin
from django.contrib.auth.models import Group, User
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
User = django.contrib.auth.get_user_model()
admin.site.unregister(Group)
class TeamInline(admin.TabularInline):

View File

@ -3,7 +3,7 @@ from django.db.models import Q
from app import settings
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from core.forms.common import CommonModelForm

View File

@ -9,11 +9,13 @@ def permission_queryset():
apps = [
'access',
'accounting',
'assistance',
'config_management',
'core',
'devops',
'django_celery_results',
'human_resources',
'itam',
'itim',
'project_management',
@ -35,14 +37,17 @@ def permission_queryset():
'add_history',
'add_organization',
'add_taskresult',
'add_ticketcommentaction',
'change_checkin',
'change_history',
'change_organization',
'change_taskresult',
'change_ticketcommentaction',
'delete_checkin',
'delete_history',
'delete_organization',
'delete_taskresult',
'delete_ticketcommentaction',
'view_checkin',
'view_history',
]

View File

@ -1,18 +1,22 @@
import django
from django.contrib.auth.middleware import (
AuthenticationMiddleware,
SimpleLazyObject,
partial,
)
from django.contrib.auth.models import User, Group
from django.contrib.auth.models import Group
from django.utils.deprecation import MiddlewareMixin
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from settings.models.app_settings import AppSettings
User = django.contrib.auth.get_user_model()
class RequestTenancy(MiddlewareMixin):
"""Access Middleware

View File

@ -0,0 +1,25 @@
# Generated by Django 5.1.9 on 2025-05-14 11:06
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('access', '0005_entity_person_entityhistory_entitynotes_role_and_more'),
('assistance', '0005_knowledgebasecategoryhistory_knowledgebasehistory'),
('config_management', '0007_configgroupshistory_configgrouphostshistory_and_more'),
('core', '0022_ticketcommentbase_ticketbase_ticketcommentsolution_and_more'),
('devops', '0011_alter_gitgroup_unique_together_and_more'),
('itam', '0010_alter_software_organization'),
('itim', '0009_slmticket_requestticket'),
('project_management', '0005_projecthistory_projectmilestonehistory_and_more'),
('settings', '0011_appsettingshistory_externallinkhistory'),
]
operations = [
migrations.RenameModel(
old_name = 'Organization',
new_name = 'Tenant'
),
]

View File

@ -0,0 +1,47 @@
# Generated by Django 5.1.9 on 2025-05-14 13:48
import access.models.team
import access.models.tenancy
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('access', '0007_rename_organization_tenant'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AlterModelOptions(
name='tenant',
options={'ordering': ['name'], 'verbose_name': 'Tenant', 'verbose_name_plural': 'Tenants'},
),
migrations.AlterField(
model_name='entity',
name='organization',
field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'),
),
migrations.AlterField(
model_name='role',
name='organization',
field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'),
),
migrations.AlterField(
model_name='team',
name='organization',
field=models.ForeignKey(help_text='Tenant this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.tenant', validators=[access.models.team.Team.validatate_organization_exists], verbose_name='Tenant'),
),
migrations.AlterField(
model_name='tenant',
name='manager',
field=models.ForeignKey(help_text='Manager for this Tenancy', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Manager'),
),
migrations.AlterField(
model_name='tenant',
name='name',
field=models.CharField(help_text='Name of this Tenancy', max_length=50, unique=True, verbose_name='Name'),
),
]

View File

@ -0,0 +1,135 @@
from django.contrib.auth.models import ContentType, Permission
from django.db import migrations
from access.models.team import Team
ContentType.DoesNotExist
def add_tenancy_permissions(apps, schema_editor):
print('')
print(f"Begin permission migration for rename of Organization to Tenant.")
try:
add_permission = Permission.objects.get(
codename = 'add_tenant',
content_type = ContentType.objects.get(
app_label = 'access',
model = 'tenant',
)
)
change_permission = Permission.objects.get(
codename = 'change_tenant',
content_type = ContentType.objects.get(
app_label = 'access',
model = 'tenant',
)
)
delete_permission = Permission.objects.get(
codename = 'delete_tenant',
content_type = ContentType.objects.get(
app_label = 'access',
model = 'tenant',
)
)
view_permission = Permission.objects.get(
codename = 'view_tenant',
content_type = ContentType.objects.get(
app_label = 'access',
model = 'tenant',
)
)
print(f' Searching for Teams.')
teams = Team.objects.select_related('group_ptr__permissions')
print(f'Found {str(len(teams))} Teams.')
for team in teams:
print(f' Processing Team {str(team.team_name)}.')
permissions = team.group_ptr.permissions.all()
print(f' Searching for Organization Permissions.')
print(f' Found {str(len(permissions))} Permissions.')
for permission in permissions:
if '_organization' not in permission.codename:
continue
action = str(permission.codename).split('_')[0]
print(f' Found Organization Permission {str(action)}')
if action == 'add':
team.group_ptr.permissions.add( add_permission )
print(f' Add Tenant Permission {str(action)}')
team.group_ptr.permissions.remove( permission )
print(f' Remove Organization Permission {str(action)}')
elif action == 'change':
team.group_ptr.permissions.add( change_permission )
print(f' Add Tenant Permission {str(action)}')
team.group_ptr.permissions.remove( permission )
print(f' Remove Organization Permission {str(action)}')
elif action == 'delete':
team.group_ptr.permissions.add( delete_permission )
print(f' Add Tenant Permission {str(action)}')
team.group_ptr.permissions.remove( permission )
print(f' Remove Organization Permission {str(action)}')
elif action == 'view':
team.group_ptr.permissions.add( view_permission )
print(f' Add Tenant Permission {str(action)}')
team.group_ptr.permissions.remove( permission )
print(f' Remove Organization Permission {str(action)}')
print(f' Completed Team {str(team.team_name)}.')
except ContentType.DoesNotExist:
# DB is new so no content types. no migration to be done.
pass
print(' Permission Migration Actions Complete.')
class Migration(migrations.Migration):
dependencies = [
('access', '0008_alter_tenant_options_alter_entity_organization_and_more'),
]
operations = [
migrations.RunPython(add_tenancy_permissions),
]

View File

@ -0,0 +1,43 @@
# Generated by Django 5.1.9 on 2025-05-16 09:58
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('access', '0009_migrate_organization_permission_tenant'),
]
operations = [
migrations.CreateModel(
name='Company',
fields=[
('entity_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='access.entity')),
('name', models.CharField(help_text='The name of this entity', max_length=80, verbose_name='Name')),
],
options={
'verbose_name': 'Company',
'verbose_name_plural': 'Companies',
'ordering': ['name'],
'sub_model_type': 'company',
},
bases=('access.entity',),
),
migrations.AlterField(
model_name='entity',
name='entity_type',
field=models.CharField(help_text='Type this entity is', max_length=30, verbose_name='Entity Type'),
),
migrations.AlterField(
model_name='person',
name='dob',
field=models.DateField(blank=True, help_text='The Persons Date of Birth (DOB)', null=True, verbose_name='DOB'),
),
migrations.AlterField(
model_name='person',
name='m_name',
field=models.CharField(blank=True, help_text='The persons middle name(s)', max_length=100, null=True, verbose_name='Middle Name(s)'),
),
]

View File

@ -4,7 +4,7 @@ from django.contrib.auth.models import Group
from django.core.exceptions import PermissionDenied
from django.utils.functional import cached_property
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.models.team import Team
@ -260,7 +260,7 @@ class OrganizationMixin():
self.permission_required = permissions_required
organization_manager_models = [
'access.organization',
'access.tenant',
'access.team',
'access.teamusers',
]
@ -326,7 +326,7 @@ class OrganizationMixin():
if required_permission.replace(
'view_', ''
) == 'access.organization' and len(self.kwargs) == 0:
) == 'access.tenant' and len(self.kwargs) == 0:
return True

View File

@ -1,9 +1,13 @@
from django.contrib.auth.models import User, Group
import django
from django.contrib.auth.models import Group
from django.db import models
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.models.team import Team
User = django.contrib.auth.get_user_model()
class OrganizationMixin:
@ -89,7 +93,7 @@ class OrganizationMixin:
self._obj_organization = obj.organization
elif str(self.model._meta.verbose_name).lower() == 'organization':
elif str(self.model._meta.verbose_name).lower() == 'tenant':
self._obj_organization = obj
@ -130,7 +134,7 @@ class OrganizationMixin:
parent_model (Model): with PK from kwargs['pk']
"""
return self.parent_model.objects.get(pk=self.kwargs[self.parent_model_pk_kwarg])
return self.get_parent_model().objects.get(pk=self.kwargs[self.parent_model_pk_kwarg])

View File

@ -5,7 +5,7 @@ from django.core.exceptions import ObjectDoesNotExist
from rest_framework import exceptions
from rest_framework.permissions import DjangoObjectPermissions
from access.models.tenancy import Organization, TenancyObject
from access.models.tenancy import Tenant, TenancyObject
from core import exceptions as centurion_exceptions
@ -14,10 +14,10 @@ from core import exceptions as centurion_exceptions
class OrganizationPermissionMixin(
DjangoObjectPermissions,
):
"""Organization Permission Mixin
"""Tenant Permission Mixin
This class is to be used as the permission class for API `Views`/`ViewSets`.
In combination with the `OrganizationPermissionsMixin`, permission checking
In combination with the `TenantPermissionsMixin`, permission checking
will be done to ensure the user has the correct permissions to perform the
CRUD operation.
@ -166,7 +166,7 @@ class OrganizationPermissionMixin(
raise centurion_exceptions.PermissionDenied()
obj_organization: Organization = view.get_obj_organization(
obj_organization: Tenant = view.get_obj_organization(
request = request
)

View File

@ -1,2 +1,4 @@
from . import contact
from . import company_base
from . import person
from . import role

View File

@ -0,0 +1,102 @@
from django.db import models
from access.models.entity import Entity
class Company(
Entity
):
# This model is intended to be called `Organization`, however at the time of
# creation this was not possible as Tenant (ne Organization) still has
# references in code to `organization` witch clashes with the intended name of
# this model.
class Meta:
ordering = [
'name',
]
sub_model_type = 'company'
verbose_name = 'Company'
verbose_name_plural = 'Companies'
name = models.CharField(
blank = False,
help_text = 'The name of this entity',
max_length = 80,
unique = False,
verbose_name = 'Name'
)
def __str__(self) -> str:
return self.name
documentation = ''
history_model_name = 'company'
page_layout: dict = [
{
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'organization',
'name',
],
"right": [
'model_notes',
'created',
'modified',
]
}
]
},
{
"name": "Knowledge Base",
"slug": "kb_articles",
"sections": [
{
"layout": "table",
"field": "knowledge_base",
}
]
},
{
"name": "Tickets",
"slug": "tickets",
"sections": [
{
"layout": "table",
"field": "tickets",
}
]
},
{
"name": "Notes",
"slug": "notes",
"sections": []
},
]
table_fields: list = [
'name',
'organization',
'created',
]
def clean(self):
super().clean()

View File

@ -15,6 +15,8 @@ class Contact(
'email',
]
sub_model_type = 'contact'
verbose_name = 'Contact'
verbose_name_plural = 'Contacts'
@ -42,6 +44,8 @@ class Contact(
documentation = ''
history_model_name = 'contact'
page_layout: list = [
{
"name": "Details",

View File

@ -22,6 +22,8 @@ class Entity(
'organization',
]
sub_model_type = 'entity'
verbose_name = 'Entity'
verbose_name_plural = 'Entities'
@ -35,9 +37,9 @@ class Entity(
verbose_name = 'ID'
)
entity_type = models.CharField(
blank = False,
default = Meta.verbose_name.lower(),
help_text = 'Type this entity is',
max_length = 30,
unique = False,
@ -93,6 +95,10 @@ class Entity(
for related_object in getattr(meta, 'related_objects', []):
if not issubclass(related_object.related_model, Entity):
continue
if getattr(self, related_object.name, None):
if(
@ -125,15 +131,17 @@ class Entity(
if related_model_name == '':
return None
related_model = None
elif related_model is None:
return self
related_model = self
elif related_model.get_related_field_name() != '':
elif hasattr(related_model, 'get_related_field_name'):
related_model = related_model.get_related_model()
if related_model.get_related_field_name() != '':
related_model = related_model.get_related_model()
return related_model

View File

@ -1,155 +1 @@
from django.db import models
from django.contrib.auth.models import User
from rest_framework.reverse import reverse
from access.fields import (
AutoCreatedField,
AutoLastModifiedField,
AutoSlugField
)
from core.mixin.history_save import SaveHistory
class Organization(SaveHistory):
class Meta:
verbose_name = "Organization"
verbose_name_plural = "Organizations"
ordering = ['name']
def save(self, *args, **kwargs):
if self.slug == '_':
self.slug = self.name.lower().replace(' ', '_')
super().save(*args, **kwargs)
id = models.AutoField(
blank=False,
help_text = 'ID of this item',
primary_key=True,
unique=True,
verbose_name = 'ID'
)
name = models.CharField(
blank = False,
help_text = 'Name of this Organization',
max_length = 50,
unique = True,
verbose_name = 'Name'
)
manager = models.ForeignKey(
User,
blank = False,
help_text = 'Manager for this organization',
null = True,
on_delete=models.SET_NULL,
verbose_name = 'Manager'
)
model_notes = models.TextField(
blank = True,
default = None,
help_text = 'Tid bits of information',
null= True,
verbose_name = 'Notes',
)
slug = AutoSlugField()
created = AutoCreatedField()
modified = AutoLastModifiedField()
def get_organization(self):
return self
def __int__(self):
return self.id
def __str__(self):
return self.name
table_fields: list = [
'nbsp',
'name',
'created',
'modified',
'nbsp'
]
page_layout: list = [
{
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'name',
'manager',
'created',
'modified',
],
"right": [
'model_notes',
]
}
]
},
{
"name": "Teams",
"slug": "teams",
"sections": [
{
"layout": "table",
"field": "teams"
}
]
},
{
"name": "Knowledge Base",
"slug": "kb_articles",
"sections": [
{
"layout": "table",
"field": "knowledge_base",
}
]
},
{
"name": "Notes",
"slug": "notes",
"sections": []
}
]
def get_url( self, request = None ) -> str:
if request:
return reverse("v2:_api_v2_organization-detail", request=request, kwargs={'pk': self.id})
return reverse("v2:_api_v2_organization-detail", kwargs={'pk': self.id})
def save_history(self, before: dict, after: dict) -> bool:
from access.models.organization_history import OrganizationHistory
history = super().save_history(
before = before,
after = after,
history_model = OrganizationHistory
)
return history
from .tenant import Tenant as Organization

View File

@ -2,7 +2,7 @@ from django.db import models
from core.models.model_history import ModelHistory
from access.models.organization import Organization
from access.models.tenant import Tenant
@ -23,7 +23,7 @@ class OrganizationHistory(
model = models.ForeignKey(
Organization,
Tenant,
blank = False,
help_text = 'Model this note belongs to',
null = False,
@ -46,8 +46,8 @@ class OrganizationHistory(
model = None
from access.serializers.organization import OrganizationBaseSerializer
from access.serializers.organization import TenantBaseSerializer
model = OrganizationBaseSerializer(self.model, context = serializer_context)
model = TenantBaseSerializer(self.model, context = serializer_context)
return model

View File

@ -1,6 +1,6 @@
from django.db import models
from access.models.organization import Organization
from access.models.tenant import Tenant
from core.models.model_notes import ModelNotes
@ -23,7 +23,7 @@ class OrganizationNotes(
model = models.ForeignKey(
Organization,
Tenant,
blank = False,
help_text = 'Model this note belongs to',
null = False,

View File

@ -20,6 +20,8 @@ class Person(
'dob',
]
sub_model_type = 'person'
verbose_name = 'Person'
verbose_name_plural = 'People'
@ -34,7 +36,6 @@ class Person(
m_name = models.CharField(
blank = True,
default = None,
help_text = 'The persons middle name(s)',
max_length = 100,
null = True,
@ -52,7 +53,6 @@ class Person(
dob = models.DateField(
blank = True,
default = None,
help_text = 'The Persons Date of Birth (DOB)',
null = True,
unique = False,
@ -65,6 +65,8 @@ class Person(
documentation = ''
history_model_name = 'person'
page_layout: dict = []
table_fields: list = [

View File

@ -8,7 +8,7 @@ from access.fields import (
AutoLastModifiedField
)
from access.models.organization import Organization
from access.models.tenant import Tenant
from access.models.tenancy import TenancyObject
from core import exceptions as centurion_exceptions
@ -27,7 +27,9 @@ class Team(Group, TenancyObject):
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
self.name = self.organization.name.lower().replace(' ', '_') + '_' + self.team_name.lower().replace(' ', '_')
if self.organization_id:
self.name = self.organization.name.lower().replace(' ', '_') + '_' + self.team_name.lower().replace(' ', '_')
super().save(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields)
@ -53,13 +55,13 @@ class Team(Group, TenancyObject):
)
organization = models.ForeignKey(
Organization,
Tenant,
blank = False,
help_text = 'Organization this belongs to',
help_text = 'Tenant this belongs to',
null = False,
on_delete = models.CASCADE,
validators = [validatate_organization_exists],
verbose_name = 'Organization'
verbose_name = 'Tenant'
)
created = AutoCreatedField()

View File

@ -1,6 +1,8 @@
import django
from django.conf import settings
from django.db import models
from django.contrib.auth.models import User, Group
from django.contrib.auth.models import Group
from rest_framework.reverse import reverse
@ -9,12 +11,14 @@ from access.fields import (
AutoLastModifiedField
)
from access.models.organization import Organization
from access.models.tenant import Tenant
from access.models.team import Team
from core.lib.feature_not_used import FeatureNotUsed
from core.mixin.history_save import SaveHistory
User = django.contrib.auth.get_user_model()
class TeamUsers(SaveHistory):
@ -95,7 +99,7 @@ class TeamUsers(SaveHistory):
user.groups.remove(group)
def get_organization(self) -> Organization:
def get_organization(self) -> Tenant:
return self.team.organization

View File

@ -1,12 +1,11 @@
# from django.conf import settings
import django
import logging
from django.db import models
# from django.contrib.auth.models import User, Group
from rest_framework.reverse import reverse
# from .fields import *
from access.models.organization import Organization
from access.models.tenant import Tenant
from core import exceptions as centurion_exceptions
from core.middleware.get_request import get_request
@ -137,14 +136,14 @@ class TenancyObject(SaveHistory):
)
organization = models.ForeignKey(
Organization,
Tenant,
blank = False,
help_text = 'Organization this belongs to',
help_text = 'Tenancy this belongs to',
null = False,
on_delete = models.CASCADE,
related_name = '+',
validators = [validatate_organization_exists],
verbose_name = 'Organization'
verbose_name = 'Tenant'
)
is_global = models.BooleanField(
@ -162,7 +161,7 @@ class TenancyObject(SaveHistory):
verbose_name = 'Notes',
)
def get_organization(self) -> Organization:
def get_organization(self) -> Tenant:
return self.organization
app_namespace: str = None
@ -193,6 +192,18 @@ class TenancyObject(SaveHistory):
only be used when there is model inheritence.
"""
_log: logging.Logger = None
def get_log(self):
if self._log is None:
self._log = logging.getLogger('centurion.' + self._meta.app_label)
return self._log
page_layout: list = None
note_basename: str = None
"""URL BaseName for the notes endpoint.
@ -280,7 +291,7 @@ class TenancyObject(SaveHistory):
raise centurion_exceptions.ValidationError(
detail = {
'organization': 'Organization is required'
'organization': 'Tenant is required'
},
code = 'required'
)

161
app/access/models/tenant.py Normal file
View File

@ -0,0 +1,161 @@
import django
from django.conf import settings
from django.db import models
from rest_framework.reverse import reverse
from access.fields import (
AutoCreatedField,
AutoLastModifiedField,
AutoSlugField
)
from core.mixin.history_save import SaveHistory
User = django.contrib.auth.get_user_model()
class Tenant(SaveHistory):
class Meta:
verbose_name = "Tenant"
verbose_name_plural = "Tenants"
ordering = ['name']
def save(self, *args, **kwargs):
if self.slug == '_':
self.slug = self.name.lower().replace(' ', '_')
super().save(*args, **kwargs)
id = models.AutoField(
blank=False,
help_text = 'ID of this item',
primary_key=True,
unique=True,
verbose_name = 'ID'
)
name = models.CharField(
blank = False,
help_text = 'Name of this Tenancy',
max_length = 50,
unique = True,
verbose_name = 'Name'
)
manager = models.ForeignKey(
settings.AUTH_USER_MODEL,
blank = False,
help_text = 'Manager for this Tenancy',
null = True,
on_delete=models.SET_NULL,
verbose_name = 'Manager'
)
model_notes = models.TextField(
blank = True,
default = None,
help_text = 'Tid bits of information',
null= True,
verbose_name = 'Notes',
)
slug = AutoSlugField()
created = AutoCreatedField()
modified = AutoLastModifiedField()
def get_organization(self):
return self
def __int__(self):
return self.id
def __str__(self):
return self.name
table_fields: list = [
'nbsp',
'name',
'created',
'modified',
'nbsp'
]
page_layout: list = [
{
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'name',
'manager',
'created',
'modified',
],
"right": [
'model_notes',
]
}
]
},
{
"name": "Teams",
"slug": "teams",
"sections": [
{
"layout": "table",
"field": "teams"
}
]
},
{
"name": "Knowledge Base",
"slug": "kb_articles",
"sections": [
{
"layout": "table",
"field": "knowledge_base",
}
]
},
{
"name": "Notes",
"slug": "notes",
"sections": []
}
]
def get_url( self, request = None ) -> str:
if request:
return reverse("v2:_api_v2_organization-detail", request=request, kwargs={'pk': self.id})
return reverse("v2:_api_v2_organization-detail", kwargs={'pk': self.id})
def save_history(self, before: dict, after: dict) -> bool:
from access.models.organization_history import OrganizationHistory
history = super().save_history(
before = before,
after = after,
history_model = OrganizationHistory
)
return history
Organization = Tenant

View File

@ -6,7 +6,7 @@ from access.models.entity import Entity
from api.serializers import common
from access.serializers.organization import OrganizationBaseSerializer
from access.serializers.organization import TenantBaseSerializer
@ -87,4 +87,4 @@ class ModelSerializer(
class ViewSerializer(ModelSerializer):
"""Entity Base View Model"""
organization = OrganizationBaseSerializer(many=False, read_only=True)
organization = TenantBaseSerializer(many=False, read_only=True)

View File

@ -0,0 +1,70 @@
from drf_spectacular.utils import extend_schema_serializer
from access.models.company_base import Company
from access.serializers.entity import (
BaseSerializer as BaseBaseSerializer,
ModelSerializer as BaseModelSerializer,
)
from access.serializers.organization import TenantBaseSerializer
class BaseSerializer(
BaseBaseSerializer,
):
pass
@extend_schema_serializer(component_name = 'CompanyEntityModelSerializer')
class ModelSerializer(
BaseSerializer,
BaseModelSerializer,
):
"""Company Model
This model inherits from the Entity base model.
"""
class Meta:
model = Company
fields = [
'id',
'entity_ptr_id',
'organization',
'entity_type',
'display_name',
'name',
'model_notes',
'is_global',
'created',
'modified',
'_urls',
]
read_only_fields = [
'id',
'display_name',
'entity_type',
'created',
'modified',
'_urls',
]
@extend_schema_serializer(component_name = 'CompanyEntityViewSerializer')
class ViewSerializer(
ModelSerializer,
):
"""Company View Model
This model inherits from the Entity base model.
"""
organization = TenantBaseSerializer(many=False, read_only=True)

View File

@ -2,11 +2,11 @@ from drf_spectacular.utils import extend_schema_serializer
from access.models.contact import Contact
from access.serializers.person import (
from access.serializers.entity_person import (
BaseSerializer as BaseBaseSerializer,
ModelSerializer as BaseModelSerializer,
)
from access.serializers.organization import OrganizationBaseSerializer
from access.serializers.organization import TenantBaseSerializer
@ -72,4 +72,4 @@ class ViewSerializer(
This model inherits from the Person model.
"""
organization = OrganizationBaseSerializer(many=False, read_only=True)
organization = TenantBaseSerializer(many=False, read_only=True)

View File

@ -6,7 +6,7 @@ from access.serializers.entity import (
BaseSerializer as BaseBaseSerializer,
ModelSerializer as BaseModelSerializer,
)
from access.serializers.organization import OrganizationBaseSerializer
from access.serializers.organization import TenantBaseSerializer
@ -70,4 +70,4 @@ class ViewSerializer(
This model inherits from the Entity base model.
"""
organization = OrganizationBaseSerializer(many=False, read_only=True)
organization = TenantBaseSerializer(many=False, read_only=True)

View File

@ -2,15 +2,16 @@ from rest_framework.reverse import reverse
from rest_framework import serializers
from access.models.organization import Organization
from access.models.tenant import Tenant
from app.serializers.user import UserBaseSerializer
from core import fields as centurion_field
Organization = Tenant
class OrganizationBaseSerializer(serializers.ModelSerializer):
class TenantBaseSerializer(serializers.ModelSerializer):
display_name = serializers.SerializerMethodField('get_display_name')
@ -24,7 +25,7 @@ class OrganizationBaseSerializer(serializers.ModelSerializer):
class Meta:
model = Organization
model = Tenant
fields = [
'id',
@ -42,8 +43,8 @@ class OrganizationBaseSerializer(serializers.ModelSerializer):
class OrganizationModelSerializer(
OrganizationBaseSerializer
class TenantModelSerializer(
TenantBaseSerializer
):
_urls = serializers.SerializerMethodField('get_url')
@ -74,7 +75,7 @@ class OrganizationModelSerializer(
class Meta:
model = Organization
model = Tenant
fields = '__all__'
@ -98,7 +99,7 @@ class OrganizationModelSerializer(
]
class OrganizationViewSerializer(OrganizationModelSerializer):
class TenantViewSerializer(TenantModelSerializer):
pass
manager = UserBaseSerializer(many=False, read_only = True)

View File

@ -5,7 +5,7 @@ from drf_spectacular.utils import extend_schema_serializer
from access.functions.permissions import permission_queryset
from access.models.role import Role
from access.serializers.organization import OrganizationBaseSerializer
from access.serializers.organization import TenantBaseSerializer
from api.serializers import common
@ -109,6 +109,6 @@ class ModelSerializer(
class ViewSerializer(ModelSerializer):
"""Role Base View Model"""
organization = OrganizationBaseSerializer( many=False, read_only=True )
organization = TenantBaseSerializer( many=False, read_only=True )
permissions = PermissionBaseSerializer( many=True, read_only=True )

View File

@ -7,7 +7,7 @@ from access.models.team import Team
from api.serializers import common
from access.functions.permissions import permission_queryset
from access.serializers.organization import OrganizationBaseSerializer
from access.serializers.organization import TenantBaseSerializer
from app.serializers.permission import Permission, PermissionBaseSerializer
@ -127,6 +127,6 @@ class TeamModelSerializer(
class TeamViewSerializer(TeamModelSerializer):
organization = OrganizationBaseSerializer(many=False, read_only=True)
organization = TenantBaseSerializer(many=False, read_only=True)
permissions = PermissionBaseSerializer(many = True)

View File

@ -1,7 +1,3 @@
import pytest
import unittest
from access.models.tenancy import TenancyManager
@ -19,83 +15,18 @@ class TenancyObject:
"""
def test_history_save(self):
"""Confirm the desired intent for saving model history."""
# def test_history_save(self):
# """Confirm the desired intent for saving model history."""
assert self.model.save_model_history == self.should_model_history_be_saved
def test_has_attr_get_organization(self):
""" TenancyObject attribute check
TenancyObject has function get_organization
"""
assert hasattr(self.model, 'get_organization')
def test_has_attr_is_global(self):
""" TenancyObject attribute check
TenancyObject has field is_global
"""
assert hasattr(self.model, 'is_global')
# assert self.model.save_model_history == self.should_model_history_be_saved
def test_has_attr_model_notes(self):
""" TenancyObject attribute check
# @pytest.mark.skip(reason="to be written")
# def test_edit_no_organization_fails(self):
# """ Devices must be assigned an organization
TenancyObject has field model_notes
"""
# Must not be able to edit an item without an organization
# """
# pass
assert hasattr(self.model, 'model_notes')
def test_has_attr_organization(self):
""" TenancyObject attribute check
TenancyObject has field organization
"""
assert hasattr(self.model, 'organization')
@pytest.mark.skip(reason="to be written")
def test_create_no_organization_fails(self):
""" Devices must be assigned an organization
Must not be able to create an item without an organization
"""
pass
@pytest.mark.skip(reason="to be written")
def test_edit_no_organization_fails(self):
""" Devices must be assigned an organization
Must not be able to edit an item without an organization
"""
pass
def test_has_attr_organization(self):
""" TenancyObject attribute check
TenancyObject has function objects
"""
assert hasattr(self.model, 'objects')
def test_attribute_is_type_objects(self):
""" Attribute Check
attribute `objects` must be set to `access.models.TenancyManager()`
"""
assert type(self.model.objects) is TenancyManager

View File

@ -0,0 +1,24 @@
import pytest
from access.models.company_base import Company
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Company
yield request.cls.model
del request.cls.model
@pytest.fixture(scope='function')
def create_serializer():
from access.serializers.entity_company import ModelSerializer
yield ModelSerializer

View File

@ -0,0 +1,72 @@
from django.test import TestCase
from access.models.company_base import Company
from access.tests.functional.entity.test_functional_entity_metadata import (
EntityMetadataInheritedCases
)
class CompanyMetadataTestCases(
EntityMetadataInheritedCases,
):
add_data: dict = {
'name': 'Ian1'
}
kwargs_create_item: dict = {
'name': 'Ian2',
}
kwargs_create_item_diff_org: dict = {
'name': 'Ian3',
}
model = Company
class CompanyMetadataInheritedCases(
CompanyMetadataTestCases,
):
model = None
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
@classmethod
def setUpTestData(self):
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
# self.url_kwargs = {
# 'entity_model': self.model._meta.sub_model_type
# }
# self.url_view_kwargs = {
# 'entity_model': self.model._meta.sub_model_type
# }
super().setUpTestData()
class CompanyMetadataTest(
CompanyMetadataTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,43 @@
import pytest
from access.tests.functional.entity.test_functional_entity_permission import (
EntityPermissionsAPIInheritedCases
)
class CompanyPermissionsAPITestCases(
EntityPermissionsAPIInheritedCases,
):
add_data: dict = {
'name': 'Ian1',
}
kwargs_create_item: dict = {
'name': 'Ian2',
}
kwargs_create_item_diff_org: dict = {
'name': 'Ian3',
}
class CompanyPermissionsAPIInheritedCases(
CompanyPermissionsAPITestCases,
):
add_data: dict = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
class CompanyPermissionsAPIPyTest(
CompanyPermissionsAPITestCases,
):
pass

View File

@ -0,0 +1,46 @@
import pytest
from rest_framework.exceptions import ValidationError
from access.tests.functional.entity.test_functional_entity_serializer import (
MockView,
EntitySerializerInheritedCases
)
class CompanySerializerTestCases(
EntitySerializerInheritedCases
):
parameterized_test_data: dict = {
"name": {
'will_create': False,
'exception_key': 'required'
},
}
valid_data: dict = {
'name': 'Ian',
}
"""Valid data used by serializer to create object"""
class CompanySerializerInheritedCases(
CompanySerializerTestCases,
):
parameterized_test_data: dict = None
valid_data: dict = None
"""Valid data used by serializer to create object"""
class CompanySerializerPyTest(
CompanySerializerTestCases,
):
parameterized_test_data: dict = None

View File

@ -0,0 +1,58 @@
from django.test import TestCase
from access.models.company_base import Company
from access.tests.functional.entity.test_functional_entity_viewset import (
EntityViewSetInheritedCases
)
class ViewSetTestCases(
EntityViewSetInheritedCases,
):
add_data: dict = {
'name': 'Ian',
}
kwargs_create_item: dict = {
'name': 'Ian2',
}
kwargs_create_item_diff_org: dict = {
'name': 'Ian3',
}
model = Company
class CompanyViewSetInheritedCases(
ViewSetTestCases,
):
model = None
@classmethod
def setUpTestData(self):
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
super().setUpTestData()
class CompanyViewSetTest(
ViewSetTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,24 @@
import pytest
from access.models.contact import Contact
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Contact
yield request.cls.model
del request.cls.model
@pytest.fixture(scope='function')
def create_serializer():
from access.serializers.entity_contact import ModelSerializer
yield ModelSerializer

View File

@ -0,0 +1,65 @@
from django.test import TestCase
from access.models.contact import Contact
from access.tests.functional.person.test_functional_person_metadata import (
PersonMetadataInheritedCases
)
class ContactMetadataTestCases(
PersonMetadataInheritedCases,
):
add_data: dict = {
'email': 'ipfunny@unit.test',
}
kwargs_create_item: dict = {
'email': 'ipweird@unit.test',
}
kwargs_create_item_diff_org: dict = {
'email': 'ipstrange@unit.test',
}
model = Contact
class ContactMetadataInheritedCases(
ContactMetadataTestCases,
):
model = None
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
@classmethod
def setUpTestData(self):
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
super().setUpTestData()
class ContactMetadataTest(
ContactMetadataTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,70 @@
import pytest
from access.tests.functional.person.test_functional_person_permission import (
PersonPermissionsAPIInheritedCases
)
class ContactPermissionsAPITestCases(
PersonPermissionsAPIInheritedCases,
):
add_data: dict = {
'email': 'ipfunny@unit.test',
}
kwargs_create_item: dict = {
'email': 'ipweird@unit.test',
}
kwargs_create_item_diff_org: dict = {
'email': 'ipstrange@unit.test',
}
class ContactPermissionsAPIInheritedCases(
ContactPermissionsAPITestCases,
):
add_data: dict = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
# url_name = '_api_v2_entity_sub'
# @pytest.fixture(scope='class')
# def inherited_var_setup(self, request):
# request.cls.url_kwargs.update({
# 'entity_model': self.model._meta.sub_model_type
# })
# request.cls.url_view_kwargs.update({
# 'entity_model': self.model._meta.sub_model_type
# })
# @pytest.fixture(scope='class', autouse = True)
# def class_setup(self, request, django_db_blocker,
# model,
# var_setup,
# prepare,
# inherited_var_setup,
# diff_org_model,
# create_model,
# ):
# pass
class ContactPermissionsAPIPyTest(
ContactPermissionsAPITestCases,
):
pass

View File

@ -1,129 +1,46 @@
import pytest
from django.test import TestCase
from rest_framework.exceptions import ValidationError
from access.serializers.contact import (
Contact,
ModelSerializer
)
from access.tests.functional.person.test_functional_person_serializer import (
MockView,
PersonSerializerInheritedCases
)
class SerializerTestCases(
PersonSerializerInheritedCases,
class ContactSerializerTestCases(
PersonSerializerInheritedCases
):
duplicate_f_name_l_name_dob = {
'email': 'contactentityduplicateone@unit.test',
parameterized_test_data: dict = {
"email": {
'will_create': False,
'exception_key': 'required'
}
}
kwargs_create_item: dict = {
'email': 'ipfunny@unit.test',
}
kwargs_create_item_duplicate_f_name_l_name_dob = {
'email': 'contactentityduplicatetwo@unit.test',
}
model = Contact
"""Model to test"""
create_model_serializer = ModelSerializer
"""Serializer to test"""
valid_data: dict = {
'email': 'ipweird@unit.test',
'email': 'contactentityduplicatetwo@unit.test',
}
def test_serializer_validation_no_email_exception(self):
"""Serializer Validation Check
Ensure that when creating with valid data and field email is missing
a validation error occurs.
"""
data = self.valid_data.copy()
del data['email']
with pytest.raises(ValidationError) as err:
serializer = self.create_model_serializer(
data = data
)
serializer.is_valid(raise_exception = True)
assert err.value.get_codes()['email'][0] == 'required'
"""Valid data used by serializer to create object"""
class ContactSerializerInheritedCases(
SerializerTestCases,
ContactSerializerTestCases,
):
create_model_serializer = None
"""Serializer to test"""
duplicate_f_name_l_name_dob: dict = None
""" Duplicate model serializer dict
used for testing for duplicate f_name, l_name and dob fields.
"""
kwargs_create_item: dict = None
""" Model kwargs to create item"""
kwargs_create_item_duplicate_f_name_l_name_dob: dict = None
"""model kwargs to create object
**None:** Ensure that the fields of sub-model to person do not match
`self.duplicate_f_name_l_name_dob`. if they do the wrong exception will be thrown.
used for testing for duplicate f_name, l_name and dob fields.
"""
model = None
"""Model to test"""
parameterized_test_data: dict = None
valid_data: dict = None
"""Valid data used by serializer to create object"""
@classmethod
def setUpTestData(self):
"""Setup Test"""
self.duplicate_f_name_l_name_dob.update(
super().duplicate_f_name_l_name_dob
)
self.kwargs_create_item_duplicate_f_name_l_name_dob.update(
super().kwargs_create_item_duplicate_f_name_l_name_dob
)
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.valid_data.update(
super().valid_data
)
super().setUpTestData()
class ContactSerializerTest(
SerializerTestCases,
TestCase,
class ContactSerializerPyTest(
ContactSerializerTestCases,
):
pass
parameterized_test_data: dict = None

View File

@ -2,91 +2,28 @@ from django.test import TestCase
from access.models.contact import Contact
from access.tests.functional.person.test_functional_person_viewset import (
PersonMetadataInheritedCases,
PersonPermissionsAPIInheritedCases,
PersonViewSetInheritedCases
)
class ViewSetBase:
add_data = {
'email': 'ipfunny@unit.test',
}
kwargs_create_item_diff_org = {
'email': 'ipstrange@unit.test',
}
kwargs_create_item = {
'email': 'ipweird@unit.test',
}
model = Contact
url_kwargs: dict = {}
url_view_kwargs: dict = {}
class PermissionsAPITestCases(
ViewSetBase,
PersonPermissionsAPIInheritedCases,
):
pass
class ContactPermissionsAPIInheritedCases(
PermissionsAPITestCases,
):
add_data: dict = None
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
@classmethod
def setUpTestData(self):
self.add_data.update(
super().add_data
)
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.kwargs_create_item_diff_org.update(
super().kwargs_create_item_diff_org
)
super().setUpTestData()
class ContactPermissionsAPITest(
PermissionsAPITestCases,
TestCase,
):
pass
class ViewSetTestCases(
ViewSetBase,
PersonViewSetInheritedCases,
):
pass
add_data: dict = {
'email': 'ipfunny@unit.test',
}
kwargs_create_item: dict = {
'email': 'ipweird@unit.test',
}
kwargs_create_item_diff_org: dict = {
'email': 'ipstrange@unit.test',
}
model = Contact
@ -96,21 +33,19 @@ class ContactViewSetInheritedCases(
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
@classmethod
def setUpTestData(self):
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org.update(
super().kwargs_create_item_diff_org
)
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
super().setUpTestData()
@ -120,50 +55,4 @@ class ContactViewSetTest(
ViewSetTestCases,
TestCase,
):
pass
class MetadataTestCases(
ViewSetBase,
PersonMetadataInheritedCases,
):
pass
class ContactMetadataInheritedCases(
MetadataTestCases,
):
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
@classmethod
def setUpTestData(self):
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.kwargs_create_item_diff_org.update(
super().kwargs_create_item_diff_org
)
super().setUpTestData()
class ContactMetadataTest(
MetadataTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,24 @@
import pytest
from access.models.entity import Entity
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Entity
yield request.cls.model
del request.cls.model
@pytest.fixture(scope='function')
def create_serializer():
from access.serializers.entity import ModelSerializer
yield ModelSerializer

View File

@ -0,0 +1,260 @@
import django
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from access.models.entity import Entity
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from accounting.models.asset_base import AssetBase
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
User = django.contrib.auth.get_user_model()
class EntityMetadataTestCases(
MetadataAttributesFunctional,
):
add_data: dict = {}
app_namespace = 'v2'
base_model = Entity
"""Base model for this sub model
don't change or override this value
"""
change_data = None
delete_data = {}
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
model = Entity
url_kwargs: dict = {}
url_view_kwargs: dict = {}
url_name = None
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an organization for user and item
. create an organization that is different to item
2. Create a team
3. create teams with each permission: view, add, change, delete
4. create a user per team
"""
organization = Organization.objects.create(name='test_org')
self.organization = organization
self.different_organization = Organization.objects.create(name='test_different_organization')
self.view_user = User.objects.create_user(username="test_user_view", password="password")
self.item = self.model.objects.create(
organization = organization,
**self.kwargs_create_item
)
self.other_org_item = self.model.objects.create(
organization = self.different_organization,
**self.kwargs_create_item_diff_org
)
self.url_view_kwargs.update({ 'pk': self.item.id })
if self.add_data is not None:
self.add_data.update({
'organization': self.organization.id,
})
view_permissions = Permission.objects.get(
codename = 'view_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
view_team = Team.objects.create(
team_name = 'view_team',
organization = organization,
)
view_team.permissions.set([view_permissions])
add_permissions = Permission.objects.get(
codename = 'add_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
add_team = Team.objects.create(
team_name = 'add_team',
organization = organization,
)
add_team.permissions.set([add_permissions])
change_permissions = Permission.objects.get(
codename = 'change_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
change_team = Team.objects.create(
team_name = 'change_team',
organization = organization,
)
change_team.permissions.set([change_permissions])
delete_permissions = Permission.objects.get(
codename = 'delete_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
delete_team = Team.objects.create(
team_name = 'delete_team',
organization = organization,
)
delete_team.permissions.set([delete_permissions])
self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password")
TeamUsers.objects.create(
team = view_team,
user = self.view_user
)
self.add_user = User.objects.create_user(username="test_user_add", password="password")
TeamUsers.objects.create(
team = add_team,
user = self.add_user
)
self.change_user = User.objects.create_user(username="test_user_change", password="password")
TeamUsers.objects.create(
team = change_team,
user = self.change_user
)
self.delete_user = User.objects.create_user(username="test_user_delete", password="password")
TeamUsers.objects.create(
team = delete_team,
user = self.delete_user
)
self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password")
different_organization_team = Team.objects.create(
team_name = 'different_organization_team',
organization = self.different_organization,
)
different_organization_team.permissions.set([
view_permissions,
add_permissions,
change_permissions,
delete_permissions,
])
TeamUsers.objects.create(
team = different_organization_team,
user = self.different_organization_user
)
def test_sanity_is_entity_sub_model(self):
"""Sanity Test
This test ensures that the model being tested `self.model` is a
sub-model of `self.base_model`.
This test is required as the same viewset is used for all sub-models
of `AssetBase`
"""
assert issubclass(self.model, self.base_model)
class EntityMetadataInheritedCases(
EntityMetadataTestCases,
):
model = None
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
url_name = '_api_v2_entity_sub'
@classmethod
def setUpTestData(self):
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
self.url_kwargs = {
'entity_model': self.model._meta.sub_model_type
}
self.url_view_kwargs = {
'entity_model': self.model._meta.sub_model_type
}
super().setUpTestData()
class EntityMetadataTest(
EntityMetadataTestCases,
TestCase,
):
url_name = '_api_v2_entity'

View File

@ -0,0 +1,89 @@
import pytest
from api.tests.functional.test_functional_api_permissions import (
APIPermissionsInheritedCases,
)
class EntityPermissionsAPITestCases(
APIPermissionsInheritedCases,
):
add_data: dict = {}
app_namespace = 'v2'
change_data = {}
delete_data = {}
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
url_kwargs: dict = {}
url_name = '_api_v2_entity'
url_view_kwargs: dict = {}
def test_returned_data_from_user_and_global_organizations_only(self):
"""Check items returned
This test case is a over-ride of a test case with the same name.
This model is not a tenancy model making this test not-applicable.
Items returned from the query Must be from the users organization and
global ONLY!
"""
pass
class EntityPermissionsAPIInheritedCases(
EntityPermissionsAPITestCases,
):
add_data: dict = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
url_name = '_api_v2_entity_sub'
@pytest.fixture(scope='class')
def inherited_var_setup(self, request):
request.cls.url_kwargs.update({
'entity_model': self.model._meta.sub_model_type
})
request.cls.url_view_kwargs.update({
'entity_model': self.model._meta.sub_model_type
})
@pytest.fixture(scope='class', autouse = True)
def class_setup(self, request, django_db_blocker,
model,
var_setup,
prepare,
inherited_var_setup,
diff_org_model,
create_model,
):
pass
class EntityPermissionsAPIPyTest(
EntityPermissionsAPITestCases,
):
pass

View File

@ -1,94 +1,171 @@
import django
import pytest
from django.test import TestCase
from rest_framework.exceptions import ValidationError
from access.models.organization import Organization
from access.serializers.entity import (
Entity,
ModelSerializer
)
User = django.contrib.auth.get_user_model()
class SerializerTestCases:
class MockView:
kwargs_create_item: dict = {}
""" Model kwargs to create item"""
_has_import: bool = False
"""User Permission
model = Entity
"""Model to test"""
get_permission_required() sets this to `True` when user has import permission.
"""
create_model_serializer = ModelSerializer
"""Serializer to test"""
_has_purge: bool = False
"""User Permission
valid_data: dict = {}
get_permission_required() sets this to `True` when user has purge permission.
"""
_has_triage: bool = False
"""User Permission
get_permission_required() sets this to `True` when user has triage permission.
"""
class EntitySerializerTestCases:
parameterized_test_data: dict = {
"model_notes": {
'will_create': True,
}
}
valid_data: dict = {
'model_notes': 'model notes field'
}
"""Valid data used by serializer to create object"""
@classmethod
def setUpTestData(self):
"""Setup Test"""
self.organization = Organization.objects.create(name='test_org')
self.kwargs_create_item.update({
'model_notes': 'model notes field'
})
@pytest.fixture( scope = 'class')
def setup_data(self,
request,
model,
django_db_blocker,
organization_one,
):
self.valid_data.update({
'organization': self.organization.pk,
'model_notes': 'model notes field'
})
with django_db_blocker.unblock():
self.item = self.model.objects.create(
organization = self.organization,
**self.kwargs_create_item,
)
request.cls.organization = organization_one
valid_data = {}
for base in reversed(request.cls.__mro__):
if hasattr(base, 'valid_data'):
if base.valid_data is None:
continue
valid_data.update(**base.valid_data)
if len(valid_data) > 0:
request.cls.valid_data = valid_data
if 'organization' not in request.cls.valid_data:
request.cls.valid_data.update({
'organization': request.cls.organization.pk
})
request.cls.view_user = User.objects.create_user(username="cafs_test_user_view", password="password")
yield
with django_db_blocker.unblock():
request.cls.view_user.delete()
del request.cls.valid_data
def test_serializer_valid_data(self):
@pytest.fixture( scope = 'class', autouse = True)
def class_setup(self,
setup_data,
):
pass
def test_serializer_valid_data(self, create_serializer):
"""Serializer Validation Check
Ensure that when creating an object with valid data, no validation
error occurs.
"""
serializer = self.create_model_serializer(
view_set = MockView()
serializer = create_serializer(
context = {
'view': view_set,
},
data = self.valid_data
)
assert serializer.is_valid(raise_exception = True)
def test_serializer_validation_no_model_notes(self):
def test_serializer_valid_data_missing_field_is_valid(self, parameterized, param_key_test_data,
create_serializer,
param_value,
param_will_create,
):
"""Serializer Validation Check
Ensure that if creating and no model_notes is provided no validation
error occurs
Ensure that when creating an object with a user with import permission
and with valid data, no validation error occurs.
"""
data = self.valid_data.copy()
del data['model_notes']
valid_data = self.valid_data.copy()
serializer = self.create_model_serializer(
data = data
del valid_data[param_value]
view_set = MockView()
view_set._has_import = True
serializer = create_serializer(
context = {
'view': view_set,
},
data = valid_data
)
assert serializer.is_valid(raise_exception = True)
is_valid = serializer.is_valid(raise_exception = False)
assert (
(
not param_will_create
and param_will_create == is_valid
)
or param_will_create == is_valid
)
class EntitySerializerInheritedCases(
SerializerTestCases,
EntitySerializerTestCases,
):
create_model_serializer = None
"""Serializer to test"""
kwargs_create_item: dict = None
""" Model kwargs to create item"""
parameterized_test_data: dict = None
model = None
"""Model to test"""
@ -97,10 +174,40 @@ class EntitySerializerInheritedCases(
"""Valid data used by serializer to create object"""
def test_serializer_valid_data_missing_field_raises_exception(self, parameterized, param_key_test_data,
create_serializer,
param_value,
param_exception_key,
):
"""Serializer Validation Check
class EntitySerializerTest(
SerializerTestCases,
TestCase,
Ensure that when creating an object with a user with import permission
and with valid data, no validation error occurs.
"""
valid_data = self.valid_data.copy()
del valid_data[param_value]
view_set = MockView()
with pytest.raises(ValidationError) as err:
serializer = create_serializer(
context = {
'view': view_set,
},
data = valid_data
)
is_valid = serializer.is_valid(raise_exception = True)
assert err.value.get_codes()[param_value][0] == param_exception_key
class EntitySerializerPyTest(
EntitySerializerTestCases,
):
pass
parameterized_test_data: dict = None

View File

@ -1,37 +1,49 @@
from django.contrib.auth.models import Permission, User
import django
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from access.models.entity import Entity
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
from api.tests.abstract.api_permissions_viewset import APIPermissions
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
User = django.contrib.auth.get_user_model()
class ViewSetBase:
add_data: dict = None
add_data: dict = {
'model_notes': 'added model note'
}
app_namespace = 'v2'
base_model = Entity
"""Base model for this sub model
don't change or override this value
"""
change_data = None
delete_data = {}
kwargs_create_item: dict = {}
kwargs_create_item: dict = {
'model_notes': 'added model note'
}
kwargs_create_item_diff_org: dict = {}
kwargs_create_item_diff_org: dict = {
'model_notes': 'added model note'
}
model = None
url_kwargs: dict = None
url_kwargs: dict = {}
url_view_kwargs: dict = None
url_view_kwargs: dict = {}
url_name = None
@ -53,16 +65,15 @@ class ViewSetBase:
self.different_organization = Organization.objects.create(name='test_different_organization')
self.view_user = User.objects.create_user(username="test_user_view", password="password")
self.item = self.model.objects.create(
organization = organization,
model_notes = 'some notes',
**self.kwargs_create_item
)
self.other_org_item = self.model.objects.create(
organization = self.different_organization,
model_notes = 'some more notes',
**self.kwargs_create_item_diff_org
)
@ -71,7 +82,9 @@ class ViewSetBase:
if self.add_data is not None:
self.add_data.update({'organization': self.organization.id})
self.add_data.update({
'organization': self.organization.id,
})
view_permissions = Permission.objects.get(
@ -144,7 +157,6 @@ class ViewSetBase:
self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password")
self.view_user = User.objects.create_user(username="test_user_view", password="password")
TeamUsers.objects.create(
team = view_team,
user = self.view_user
@ -190,98 +202,16 @@ class ViewSetBase:
)
class PermissionsAPITestCases(
ViewSetBase,
APIPermissions,
):
add_data: dict = {}
change_data = {'model_notes': 'device'}
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
url_kwargs: dict = None
url_view_kwargs: dict = None
url_name = None
@classmethod
def setUpTestData(self):
self.add_data.update({ 'model_note': 'added model note' })
super().setUpTestData()
def test_returned_data_from_user_and_global_organizations_only(self):
"""Check items returned
This test case is a over-ride of a test case with the same name.
This model is not a tenancy model making this test not-applicable.
Items returned from the query Must be from the users organization and
global ONLY!
def test_sanity_is_asset_sub_model(self):
"""Sanity Test
This test ensures that the model being tested `self.model` is a
sub-model of `self.base_model`.
This test is required as the same viewset is used for all sub-models
of `Entity`
"""
pass
class EntityPermissionsAPIInheritedCases(
PermissionsAPITestCases,
):
add_data: dict = None
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
url_name = '_api_v2_entity_sub'
@classmethod
def setUpTestData(self):
self.url_kwargs = {
'entity_model': self.model._meta.model_name
}
self.url_view_kwargs = {
'entity_model': self.model._meta.model_name
}
super().setUpTestData()
class EntityPermissionsAPITest(
PermissionsAPITestCases,
TestCase,
):
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
model = Entity
url_kwargs: dict = {}
url_view_kwargs: dict = {}
url_name = '_api_v2_entity'
assert issubclass(self.model, self.base_model)
@ -290,17 +220,7 @@ class ViewSetTestCases(
SerializersTestCases,
):
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
model = None
url_kwargs: dict = None
url_view_kwargs: dict = None
url_name = None
model = Entity
@ -310,22 +230,28 @@ class EntityViewSetInheritedCases(
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
url_name = '_api_v2_entity_sub'
@classmethod
def setUpTestData(self):
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
self.url_kwargs = {
'entity_model': self.model._meta.model_name
'entity_model': self.model._meta.sub_model_type
}
self.url_view_kwargs = {
'entity_model': self.model._meta.model_name
'entity_model': self.model._meta.sub_model_type
}
super().setUpTestData()
@ -337,168 +263,4 @@ class EntityViewSetTest(
TestCase,
):
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
model = Entity
url_kwargs: dict = {}
url_view_kwargs: dict = {}
url_name = '_api_v2_entity'
class MetadataTestCases(
ViewSetBase,
MetadataAttributesFunctional,
):
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
model = None
url_kwargs: dict = None
url_view_kwargs: dict = None
url_name = None
class EntityMetadataInheritedCases(
MetadataTestCases,
):
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
url_name = '_api_v2_entity_sub'
@classmethod
def setUpTestData(self):
self.url_kwargs = {
'entity_model': self.model._meta.model_name
}
self.url_view_kwargs = {
'entity_model': self.model._meta.model_name
}
super().setUpTestData()
class EntityMetadataTest(
MetadataTestCases,
TestCase,
):
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
model = Entity
url_kwargs: dict = {}
url_view_kwargs: dict = {}
url_name = '_api_v2_entity'
# def test_method_options_request_detail_data_has_key_urls_back(self):
# """Test HTTP/Options Method
# Ensure the request data returned has key `urls.back`
# """
# client = Client()
# client.force_login(self.view_user)
# response = client.options(
# reverse(
# self.app_namespace + ':' + self.url_name + '-detail',
# kwargs=self.url_view_kwargs
# ),
# content_type='application/json'
# )
# assert 'back' in response.data['urls']
# def test_method_options_request_detail_data_key_urls_back_is_str(self):
# """Test HTTP/Options Method
# Ensure the request data key `urls.back` is str
# """
# client = Client()
# client.force_login(self.view_user)
# response = client.options(
# reverse(
# self.app_namespace + ':' + self.url_name + '-detail',
# kwargs=self.url_view_kwargs
# ),
# content_type='application/json'
# )
# assert type(response.data['urls']['back']) is str
# def test_method_options_request_list_data_has_key_urls_return_url(self):
# """Test HTTP/Options Method
# Ensure the request data returned has key `urls.return_url`
# """
# client = Client()
# client.force_login(self.view_user)
# if self.url_kwargs:
# url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
# else:
# url = reverse(self.app_namespace + ':' + self.url_name + '-list')
# response = client.options( url, content_type='application/json' )
# assert 'return_url' in response.data['urls']
# def test_method_options_request_list_data_key_urls_return_url_is_str(self):
# """Test HTTP/Options Method
# Ensure the request data key `urls.return_url` is str
# """
# client = Client()
# client.force_login(self.view_user)
# if self.url_kwargs:
# url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
# else:
# url = reverse(self.app_namespace + ':' + self.url_name + '-list')
# response = client.options( url, content_type='application/json' )
# assert type(response.data['urls']['return_url']) is str

View File

@ -1,6 +1,6 @@
from django.test import TestCase
from access.models.organization_history import Organization, OrganizationHistory
from access.models.organization_history import Tenant as Organization, OrganizationHistory
from core.tests.abstract.test_functional_history import HistoryEntriesCommon

View File

@ -1,22 +1,24 @@
import django
import pytest
from django.contrib.auth.models import User
from django.test import TestCase
from rest_framework.exceptions import ValidationError
from access.serializers.organization import (
Organization,
OrganizationModelSerializer
Tenant,
TenantModelSerializer
)
User = django.contrib.auth.get_user_model()
class OrganizationValidationAPI(
TestCase,
):
model = Organization
model = Tenant
@classmethod
def setUpTestData(self):
@ -45,7 +47,7 @@ class OrganizationValidationAPI(
Ensure that if creating and no name is provided a validation error occurs
"""
serializer = OrganizationModelSerializer(
serializer = TenantModelSerializer(
data = self.valid_data
)
@ -65,7 +67,7 @@ class OrganizationValidationAPI(
with pytest.raises(ValidationError) as err:
serializer = OrganizationModelSerializer(
serializer = TenantModelSerializer(
data = data
)
@ -85,7 +87,7 @@ class OrganizationValidationAPI(
del data['manager']
serializer = OrganizationModelSerializer(
serializer = TenantModelSerializer(
data = data
)

View File

@ -1,15 +1,16 @@
import django
import pytest
import unittest
import requests
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, Permission, User
from django.contrib.auth.models import AnonymousUser, Permission
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import reverse
from django.test import Client, TestCase
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
@ -17,6 +18,8 @@ from api.tests.abstract.api_permissions_viewset import APIPermissions
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional, MetaDataNavigationEntriesFunctional
User = django.contrib.auth.get_user_model()
class ViewSetBase:
@ -315,4 +318,4 @@ class OrganizationMetadata(
menu_id = 'access'
menu_entry_id = 'organization'
menu_entry_id = 'tenant'

View File

@ -3,7 +3,7 @@ from django.test import TestCase
from core.tests.abstract.model_notes_api_fields import ModelNotesNotesAPIFields
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.models.organization_notes import OrganizationNotes

View File

@ -0,0 +1,24 @@
import pytest
from access.models.person import Person
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Person
yield request.cls.model
del request.cls.model
@pytest.fixture(scope='function')
def create_serializer():
from access.serializers.entity_person import ModelSerializer
yield ModelSerializer

View File

@ -0,0 +1,83 @@
from django.test import TestCase
from access.models.person import Person
from access.tests.functional.entity.test_functional_entity_metadata import (
EntityMetadataInheritedCases
)
from accounting.models.asset_base import AssetBase
class PersonMetadataTestCases(
EntityMetadataInheritedCases,
):
add_data: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Strange',
'dob': '2025-04-08',
}
kwargs_create_item: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Weird',
'dob': '2025-04-08',
}
kwargs_create_item_diff_org: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Funny',
'dob': '2025-04-08',
}
model = Person
class PersonMetadataInheritedCases(
PersonMetadataTestCases,
):
model = None
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
@classmethod
def setUpTestData(self):
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
# self.url_kwargs = {
# 'entity_model': self.model._meta.sub_model_type
# }
# self.url_view_kwargs = {
# 'entity_model': self.model._meta.sub_model_type
# }
super().setUpTestData()
class PersonMetadataTest(
PersonMetadataTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,91 @@
import pytest
from access.tests.functional.entity.test_functional_entity_permission import (
EntityPermissionsAPIInheritedCases
)
class PersonPermissionsAPITestCases(
EntityPermissionsAPIInheritedCases,
):
add_data: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Strange',
'dob': '2025-04-08',
}
# app_namespace = 'v2'
# change_data = {}
# delete_data = {}
kwargs_create_item: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Weird',
'dob': '2025-04-08',
}
kwargs_create_item_diff_org: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Funny',
'dob': '2025-04-08',
}
# url_kwargs: dict = {}
# url_name = '_api_v2_entity'
# url_view_kwargs: dict = {}
class PersonPermissionsAPIInheritedCases(
PersonPermissionsAPITestCases,
):
add_data: dict = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
# url_name = '_api_v2_entity_sub'
# @pytest.fixture(scope='class')
# def inherited_var_setup(self, request):
# request.cls.url_kwargs.update({
# 'entity_model': self.model._meta.sub_model_type
# })
# request.cls.url_view_kwargs.update({
# 'entity_model': self.model._meta.sub_model_type
# })
# @pytest.fixture(scope='class', autouse = True)
# def class_setup(self, request, django_db_blocker,
# model,
# var_setup,
# prepare,
# inherited_var_setup,
# diff_org_model,
# create_model,
# ):
# pass
class PersonPermissionsAPIPyTest(
PersonPermissionsAPITestCases,
):
pass

View File

@ -1,161 +1,88 @@
import pytest
from django.test import TestCase
from rest_framework.exceptions import ValidationError
from access.serializers.person import (
Person,
ModelSerializer
)
from access.tests.functional.entity.test_functional_entity_serializer import (
MockView,
EntitySerializerInheritedCases
)
class SerializerTestCases(
EntitySerializerInheritedCases,
class PersonSerializerTestCases(
EntitySerializerInheritedCases
):
create_model_serializer = ModelSerializer
"""Serializer to test"""
duplicate_f_name_l_name_dob: dict = {
'f_name': 'fred',
'm_name': 'D',
'l_name': 'Flinstone',
'dob': '2025-04-08',
parameterized_test_data: dict = {
"model_notes": {
'will_create': True,
},
"f_name": {
'will_create': False,
'exception_key': 'required'
},
"m_name": {
'will_create': True,
},
"l_name": {
'will_create': False,
'exception_key': 'required'
},
"dob": {
'will_create': True,
}
}
kwargs_create_item_duplicate_f_name_l_name_dob: dict = {
'f_name': 'fred',
'm_name': 'D',
'l_name': 'Flinstone',
'dob': '2025-04-08',
}
kwargs_create_item: dict = {
valid_data: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Funny',
'dob': '2025-04-08',
}
model = Person
"""Model to test"""
valid_data: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Strange',
'dob': '2025-04-08',
}
"""Valid data used by serializer to create object"""
def test_serializer_validation_no_f_name_exception(self):
"""Serializer Validation Check
Ensure that when creating with valid data and field f_name is missing
a validation error occurs.
"""
data = self.valid_data.copy()
del data['f_name']
with pytest.raises(ValidationError) as err:
serializer = self.create_model_serializer(
data = data
)
serializer.is_valid(raise_exception = True)
assert err.value.get_codes()['f_name'][0] == 'required'
def test_serializer_validation_no_m_name(self):
"""Serializer Validation Check
Ensure that when creating with valid data and field f_name is missing
no validation error occurs.
"""
data = self.valid_data.copy()
del data['m_name']
serializer = self.create_model_serializer(
data = data
)
assert serializer.is_valid(raise_exception = True)
def test_serializer_validation_no_l_name_exception(self):
"""Serializer Validation Check
Ensure that when creating with valid data and field l_name is missing
a validation error occurs.
"""
data = self.valid_data.copy()
del data['l_name']
with pytest.raises(ValidationError) as err:
serializer = self.create_model_serializer(
data = data
)
serializer.is_valid(raise_exception = True)
assert err.value.get_codes()['l_name'][0] == 'required'
def test_serializer_validation_no_dob(self):
"""Serializer Validation Check
Ensure that when creating with valid data and field dob is missing
no validation error occurs.
"""
data = self.valid_data.copy()
del data['dob']
serializer = self.create_model_serializer(
data = data
)
assert serializer.is_valid(raise_exception = True)
def test_serializer_validation_duplicate_f_name_l_name_dob(self):
def test_serializer_validation_duplicate_f_name_l_name_dob(self, model, create_serializer):
"""Serializer Validation Check
Ensure that when creating with valid data and fields f_name, l_name and
dob already exists in the db a validation error occurs.
"""
self.model.objects.create(
organization = self.organization,
**self.kwargs_create_item_duplicate_f_name_l_name_dob
valid_data = self.valid_data.copy()
valid_data['f_name'] = 'duplicate'
valid_data['organization'] = self.organization
obj = model.objects.create(
**valid_data
)
data = self.duplicate_f_name_l_name_dob.copy()
valid_data['organization'] = self.organization.id
if 'email' in valid_data: # Contact Entity
valid_data['email'] = 'abc@xyz.qwe'
if 'name' in valid_data: # Company Entity
valid_data['name'] = 'diff'
if 'employee_number' in valid_data: # Employee Entity
valid_data['employee_number'] = 13579
view_set = MockView()
with pytest.raises(ValidationError) as err:
serializer = self.create_model_serializer(
data = data
serializer = create_serializer(
context = {
'view': view_set,
},
data = valid_data
)
serializer.is_valid(raise_exception = True)
@ -167,64 +94,18 @@ class SerializerTestCases(
class PersonSerializerInheritedCases(
SerializerTestCases,
PersonSerializerTestCases,
):
create_model_serializer = None
"""Serializer to test"""
duplicate_f_name_l_name_dob: dict = None
""" Duplicate model serializer dict
used for testing for duplicate f_name, l_name and dob fields.
"""
kwargs_create_item: dict = None
""" Model kwargs to create item"""
kwargs_create_item_duplicate_f_name_l_name_dob: dict = None
"""model kwargs to create object
**None:** Ensure that the fields of sub-model to person do not match
`self.duplicate_f_name_l_name_dob`. if they do the wrong exception will be thrown.
used for testing for duplicate f_name, l_name and dob fields.
"""
model = None
"""Model to test"""
parameterized_test_data: dict = None
valid_data: dict = None
"""Valid data used by serializer to create object"""
@classmethod
def setUpTestData(self):
"""Setup Test"""
self.duplicate_f_name_l_name_dob.update(
super().duplicate_f_name_l_name_dob
)
self.kwargs_create_item_duplicate_f_name_l_name_dob.update(
super().kwargs_create_item_duplicate_f_name_l_name_dob
)
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.valid_data.update(
super().valid_data
)
super().setUpTestData()
class PersonSerializerTest(
SerializerTestCases,
TestCase,
class PersonSerializerPyTest(
PersonSerializerTestCases,
):
pass
parameterized_test_data: dict = None

View File

@ -2,101 +2,38 @@ from django.test import TestCase
from access.models.person import Person
from access.tests.functional.entity.test_functional_entity_viewset import (
EntityMetadataInheritedCases,
EntityPermissionsAPIInheritedCases,
EntityViewSetInheritedCases
)
class ViewSetBase:
class ViewSetTestCases(
EntityViewSetInheritedCases,
):
add_data = {
add_data: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Strange',
'dob': '2025-04-08',
}
kwargs_create_item_diff_org = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Funny',
'dob': '2025-04-08',
}
kwargs_create_item = {
kwargs_create_item: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Weird',
'dob': '2025-04-08',
}
kwargs_create_item_diff_org: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Funny',
'dob': '2025-04-08',
}
model = Person
url_kwargs: dict = {}
url_view_kwargs: dict = {}
class PermissionsAPITestCases(
ViewSetBase,
EntityPermissionsAPIInheritedCases,
):
pass
class PersonPermissionsAPIInheritedCases(
PermissionsAPITestCases,
):
add_data: dict = None
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
@classmethod
def setUpTestData(self):
self.add_data.update(
super().add_data
)
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.kwargs_create_item_diff_org.update(
super().kwargs_create_item_diff_org
)
super().setUpTestData()
class PersonPermissionsAPITest(
PermissionsAPITestCases,
TestCase,
):
pass
class ViewSetTestCases(
ViewSetBase,
EntityViewSetInheritedCases,
):
pass
class PersonViewSetInheritedCases(
@ -105,21 +42,19 @@ class PersonViewSetInheritedCases(
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
@classmethod
def setUpTestData(self):
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org.update(
super().kwargs_create_item_diff_org
)
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
super().setUpTestData()
@ -129,50 +64,4 @@ class PersonViewSetTest(
ViewSetTestCases,
TestCase,
):
pass
class MetadataTestCases(
ViewSetBase,
EntityMetadataInheritedCases,
):
pass
class PersonMetadataInheritedCases(
MetadataTestCases,
):
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
@classmethod
def setUpTestData(self):
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.kwargs_create_item_diff_org.update(
super().kwargs_create_item_diff_org
)
super().setUpTestData()
class PersonMetadataTest(
MetadataTestCases,
TestCase,
):
pass

View File

@ -5,7 +5,7 @@ from django.test import TestCase
from rest_framework.exceptions import ValidationError
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.serializers.role import Role, ModelSerializer

View File

@ -1,11 +1,12 @@
from django.contrib.auth.models import Permission, User
import django
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.test import Client, TestCase
from rest_framework.reverse import reverse
from access.models.role import Role
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
@ -15,6 +16,8 @@ from api.tests.abstract.api_serializer_viewset import SerializersTestCases
from settings.models.app_settings import AppSettings
User = django.contrib.auth.get_user_model()
class ViewSetBase:

View File

@ -1,16 +1,17 @@
import django
import pytest
import unittest
import requests
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, Permission, User
from django.contrib.auth.models import AnonymousUser, Permission
from django.contrib.contenttypes.models import ContentType
from django.test import Client, TestCase
from rest_framework.reverse import reverse
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
@ -19,6 +20,8 @@ from api.tests.abstract.test_metadata_functional import MetadataAttributesFuncti
from api.tests.abstract.api_permissions_viewset import APIPermissions
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
User = django.contrib.auth.get_user_model()
class ViewSetBase:

View File

@ -1,6 +1,7 @@
import django
import pytest
from django.contrib.auth.models import Permission, User
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
@ -8,7 +9,7 @@ from rest_framework.exceptions import ValidationError
from access.middleware.request import Tenancy
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.serializers.teams import (
Team,
@ -17,6 +18,8 @@ from access.serializers.teams import (
from settings.models.app_settings import AppSettings
User = django.contrib.auth.get_user_model()
class MockView:

View File

@ -1,16 +1,17 @@
import django
import pytest
import unittest
import requests
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, Permission, User
from django.contrib.auth.models import AnonymousUser, Permission
from django.contrib.contenttypes.models import ContentType
from django.test import Client, TestCase
from rest_framework.reverse import reverse
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
@ -18,6 +19,8 @@ from api.tests.abstract.test_metadata_functional import MetadataAttributesFuncti
from api.tests.abstract.api_permissions_viewset import APIPermissions
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
User = django.contrib.auth.get_user_model()
class ViewSetBase:

View File

@ -1,12 +1,13 @@
import django
import pytest
from django.contrib.auth.models import Permission, User
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from rest_framework.exceptions import ValidationError
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.serializers.team_user import (
@ -14,6 +15,8 @@ from access.serializers.team_user import (
TeamUserModelSerializer
)
User = django.contrib.auth.get_user_model()
class MockView:

View File

@ -0,0 +1,14 @@
import pytest
from access.models.company_base import Company
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Company
yield request.cls.model
del request.cls.model

View File

@ -0,0 +1,37 @@
from access.tests.unit.entity.test_unit_entity_api_fields import (
EntityAPIInheritedCases
)
class CompanyAPITestCases(
EntityAPIInheritedCases,
):
parameterized_test_data = {
'name': {
'expected': str
}
}
kwargs_create_item: dict = {
'name': 'Ian'
}
class CompanyAPIInheritedCases(
CompanyAPITestCases,
):
kwargs_create_item: dict = None
model = None
class CompanyAPIPyTest(
CompanyAPITestCases,
):
pass

View File

@ -0,0 +1,105 @@
import pytest
from django.db import models
from access.models.company_base import Company
from access.tests.unit.entity.test_unit_entity_model import (
EntityModelInheritedCases
)
class CompanyModelTestCases(
EntityModelInheritedCases,
):
kwargs_create_item: dict = {
'name': 'Ian',
}
sub_model_type = 'company'
"""Sub Model Type
sub-models must have this attribute defined in `ModelName.Meta.sub_model_type`
"""
parameterized_fields: dict = {
"name": {
'field_type': models.fields.CharField,
'field_parameter_default_exists': False,
'field_parameter_verbose_name_type': str,
}
}
def test_class_inherits_company(self):
""" Class inheritence
TenancyObject must inherit SaveHistory
"""
assert issubclass(self.model, Company)
def test_attribute_value_history_app_label(self):
"""Attribute Type
history_app_label has been set, override this test case with the value
of attribute `history_app_label`
"""
assert self.model.history_app_label == 'access'
def test_attribute_value_history_model_name(self):
"""Attribute Type
history_model_name has been set, override this test case with the value
of attribute `history_model_name`
"""
assert self.model.history_model_name == 'company'
def test_function_value_get_url(self):
assert self.item.get_url() == '/api/v2/access/entity/company/' + str(self.item.id)
class CompanyModelInheritedCases(
CompanyModelTestCases,
):
"""Sub-Ticket Test Cases
Test Cases for Ticket models that inherit from model Entity
"""
kwargs_create_item: dict = {}
model = None
sub_model_type = None
"""Ticket Sub Model Type
Ticket sub-models must have this attribute defined in `ModelNam.Meta.sub_model_type`
"""
class CompanyModelPyTest(
CompanyModelTestCases,
):
def test_function_value_get_related_model(self):
"""Function test
Confirm function `get_related_model` is None for base model
"""
assert self.item.get_related_model() is None

View File

@ -0,0 +1,36 @@
from django.test import TestCase
from access.models.company_base import Company
from access.tests.unit.entity.test_unit_entity_viewset import (
EntityViewsetInheritedCases
)
class ViewsetTestCases(
EntityViewsetInheritedCases,
):
model: str = Company
class CompanyViewsetInheritedCases(
ViewsetTestCases,
):
"""Sub-Entity Test Cases
Test Cases for Entity models that inherit from model Company
"""
model: str = None
"""name of the model to test"""
class CompanyViewsetTest(
ViewsetTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,14 @@
import pytest
from access.models.contact import Contact
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Contact
yield request.cls.model
del request.cls.model

View File

@ -1,32 +0,0 @@
from django.test import TestCase
from access.models.contact import Contact
from access.tests.unit.person.test_unit_person_access_tenancy_object import (
PersonTenancyObjectInheritedCases,
)
class TenancyObjectTestCases(
PersonTenancyObjectInheritedCases,
):
model = Contact
class ContactTenancyObjectInheritedCases(
TenancyObjectTestCases,
):
"""Sub-Entity Test Cases
Test Cases for Entity models that inherit from model Contact
"""
model = None
class ContactTenancyObjectTest(
TenancyObjectTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,40 @@
from access.tests.unit.person.test_unit_person_api_fields import (
PersonAPIInheritedCases
)
class ContactAPITestCases(
PersonAPIInheritedCases,
):
parameterized_test_data = {
'email': {
'expected': str
},
'directory': {
'expected': bool
}
}
kwargs_create_item: dict = {
'email': 'ipfunny@unit.test',
}
class ContactAPIInheritedCases(
ContactAPITestCases,
):
kwargs_create_item: dict = None
model = None
class ContactAPIPyTest(
ContactAPITestCases,
):
pass

View File

@ -1,90 +0,0 @@
from django.test import TestCase
from access.models.contact import Contact
from access.tests.unit.person.test_unit_person_api_v2 import (
PersonAPIInheritedCases,
)
class APITestCases(
PersonAPIInheritedCases,
):
model = Contact
kwargs_item_create: dict = {
'email': 'ipfunny@unit.test',
}
url_ns_name = '_api_v2_entity_sub'
def test_api_field_exists_email(self):
""" Test for existance of API Field
email field must exist
"""
assert 'email' in self.api_data
def test_api_field_type_email(self):
""" Test for type for API Field
email field must be str
"""
assert type(self.api_data['email']) is str
def test_api_field_exists_directory(self):
""" Test for existance of API Field
directory field must exist
"""
assert 'directory' in self.api_data
def test_api_field_type_directory(self):
""" Test for type for API Field
directory field must be bool
"""
assert type(self.api_data['directory']) is bool
class ContactAPIInheritedCases(
APITestCases,
):
"""Sub-Entity Test Cases
Test Cases for Entity models that inherit from model Contact
"""
kwargs_item_create: dict = None
model = None
@classmethod
def setUpTestData(self):
self.kwargs_item_create.update(
super().kwargs_item_create
)
super().setUpTestData()
class ContactAPITest(
APITestCases,
TestCase,
):
pass

View File

@ -1,100 +1,110 @@
from django.db.models.fields import NOT_PROVIDED
from django.test import TestCase
import pytest
from django.db import models
from access.models.contact import Contact
from access.tests.unit.person.test_unit_person_model import (
Person,
PersonModelInheritedCases
)
class ModelTestCases(
class ContactModelTestCases(
PersonModelInheritedCases,
):
model = Contact
kwargs_item_create: dict = {
kwargs_create_item: dict = {
'email': 'ipweird@unit.test',
}
sub_model_type = 'contact'
"""Sub Model Type
sub-models must have this attribute defined in `ModelName.Meta.sub_model_type`
"""
parameterized_fields: dict = {
"email": {
'field_type': models.fields.CharField,
'field_parameter_default_exists': False,
'field_parameter_verbose_name_type': str,
},
"directory": {
'field_type': models.fields.BooleanField,
'field_parameter_default_exists': True,
'field_parameter_default_value': True,
'field_parameter_verbose_name_type': str,
}
}
def test_model_field_directory_optional(self):
"""Test Field
def test_class_inherits_contact(self):
""" Class inheritence
Field `dob` must be an optional field
TenancyObject must inherit SaveHistory
"""
assert self.model._meta.get_field('directory').blank
assert issubclass(self.model, Contact)
def test_model_field_directory_optional_default(self):
"""Test Field
# def test_attribute_value_history_app_label(self):
# """Attribute Type
Field `directory` default value is `True`
# history_app_label has been set, override this test case with the value
# of attribute `history_app_label`
# """
# assert self.model.history_app_label == 'access'
def test_attribute_value_history_model_name(self):
"""Attribute Type
history_model_name has been set, override this test case with the value
of attribute `history_model_name`
"""
assert (
self.model._meta.get_field('directory').default is True
and self.model._meta.get_field('directory').null is False
)
assert self.model.history_model_name == 'contact'
def test_model_field_email_mandatory(self):
"""Test Field
Field `email` must be a mandatory field
"""
assert(
not (
self.model._meta.get_field('email').blank
and self.model._meta.get_field('email').null
)
and self.model._meta.get_field('email').default is NOT_PROVIDED
)
def test_model_inherits_person(self):
"""Test model inheritence
model must inherit from Entity sub-model `Person`
"""
assert issubclass(self.model, Person)
def test_function_value_get_url(self):
assert self.item.get_url() == '/api/v2/access/entity/contact/' + str(self.item.id)
class ContactModelInheritedCases(
ModelTestCases,
ContactModelTestCases,
):
"""Sub-Entity Test Cases
"""Sub-Ticket Test Cases
Test Cases for Entity models that inherit from model Contact
Test Cases for Ticket models that inherit from model Entity
"""
kwargs_item_create: dict = None
kwargs_create_item: dict = {}
model = None
@classmethod
def setUpTestData(self):
self.kwargs_item_create.update(
super().kwargs_item_create
)
super().setUpTestData()
sub_model_type = None
"""Ticket Sub Model Type
Ticket sub-models must have this attribute defined in `ModelNam.Meta.sub_model_type`
"""
class ContactModelTest(
ModelTestCases,
TestCase,
class ContactModelPyTest(
ContactModelTestCases,
):
pass
def test_function_value_get_related_model(self):
"""Function test
Confirm function `get_related_model` is None for base model
"""
assert self.item.get_related_model() is None

View File

@ -1,14 +1,14 @@
from django.test import TestCase
from access.models.contact import Contact
from access.tests.unit.entity.test_unit_entity_viewset import (
EntityViewsetInheritedCases
from access.tests.unit.person.test_unit_person_viewset import (
PersonViewsetInheritedCases
)
class ViewsetTestCases(
EntityViewsetInheritedCases,
PersonViewsetInheritedCases,
):
model: str = Contact

View File

@ -0,0 +1,14 @@
import pytest
from access.models.entity import Entity
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Entity
yield request.cls.model
del request.cls.model

View File

@ -1,29 +0,0 @@
from django.test import TestCase
from access.models.entity import Entity
from access.tests.abstract.tenancy_object import TenancyObject
class TenancyObjectTestCases(
TenancyObject,
):
model = None
class EntityTenancyObjectInheritedCases(
TenancyObjectTestCases,
):
model = None
class EntityTenancyObjectTest(
TenancyObjectTestCases,
TestCase,
):
model = Entity

View File

@ -0,0 +1,78 @@
import pytest
from access.models.entity import Entity
from api.tests.unit.test_unit_api_fields import (
APIFieldsInheritedCases,
)
class EntityAPITestCases(
APIFieldsInheritedCases,
):
base_model = Entity
@pytest.fixture( scope = 'class')
def setup_model(self, request,
model,
):
if model != self.base_model:
request.cls.url_view_kwargs.update({
'entity_model': model._meta.sub_model_type,
})
@pytest.fixture( scope = 'class', autouse = True)
def class_setup(self,
setup_pre,
setup_model,
create_model,
setup_post,
):
pass
parameterized_test_data = {
'entity_type': {
'expected': str
},
'_urls.history': {
'expected': str
},
'_urls.knowledge_base': {
'expected': str
}
}
kwargs_create_item: dict = {
'entity_type': 'entity',
}
url_ns_name = '_api_v2_entity'
"""Url namespace (optional, if not required) and url name"""
class EntityAPIInheritedCases(
EntityAPITestCases,
):
kwargs_create_item: dict = None
model = None
url_ns_name = '_api_v2_entity_sub'
class EntityAPIPyTest(
EntityAPITestCases,
):
pass

View File

@ -1,215 +0,0 @@
from django.contrib.auth.models import Permission, User
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import reverse
from django.test import Client, TestCase
# from rest_framework.relations import Hyperlink
from access.models.entity import Entity
from access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from api.tests.abstract.api_fields import APITenancyObject
class APITestCases(
APITenancyObject,
):
model = None
kwargs_item_create: dict = None
url_ns_name = None
"""Url namespace (optional, if not required) and url name"""
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an organization for user and item
2. Create an item
"""
self.organization = Organization.objects.create(name='test_org')
self.item = self.model.objects.create(
organization = self.organization,
model_notes = 'random notes',
**self.kwargs_item_create
)
self.url_view_kwargs = {
'pk': self.item.id
}
if self.model._meta.model_name != 'entity':
self.url_view_kwargs.update({
'entity_model': self.item.entity_type,
})
view_permissions = Permission.objects.get(
codename = 'view_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
view_team = Team.objects.create(
team_name = 'view_team',
organization = self.organization,
)
view_team.permissions.set([view_permissions])
self.view_user = User.objects.create_user(username="test_user_view", password="password")
TeamUsers.objects.create(
team = view_team,
user = self.view_user
)
client = Client()
url = reverse('v2:' + self.url_ns_name + '-detail', kwargs=self.url_view_kwargs)
client.force_login(self.view_user)
response = client.get(url)
self.api_data = response.data
def test_api_field_exists_entity_type(self):
""" Test for existance of API Field
entity_type field must exist
"""
assert 'entity_type' in self.api_data
def test_api_field_type_entity_type(self):
""" Test for type for API Field
entity_type field must be str
"""
assert type(self.api_data['entity_type']) is str
def test_api_field_exists_url_history(self):
""" Test for existance of API Field
_urls.history field must exist
"""
assert 'history' in self.api_data['_urls']
def test_api_field_type_url_history(self):
""" Test for type for API Field
_urls.history field must be str
"""
assert type(self.api_data['_urls']['history']) is str
def test_api_field_type_url_history_value(self):
""" Test for url value
_urls.history field must use the endpoint for entity model
"""
assert str(self.api_data['_urls']['history']).endswith('/access/entity/' + str(self.item.pk) + '/history')
def test_api_field_exists_url_knowledge_base(self):
""" Test for existance of API Field
_urls.knowledge_base field must exist
"""
assert 'knowledge_base' in self.api_data['_urls']
def test_api_field_type_url_knowledge_base(self):
""" Test for type for API Field
_urls.knowledge_base field must be str
"""
assert type(self.api_data['_urls']['knowledge_base']) is str
def test_api_field_type_url_knowledge_base_value(self):
""" Test for url value
_urls.knowledge_base field must use the endpoint for entity model
"""
assert str(self.api_data['_urls']['knowledge_base']).endswith('/assistance/entity/' + str(self.item.pk) + '/knowledge_base')
class EntityAPIInheritedCases(
APITestCases,
):
kwargs_item_create: dict = None
model = None
url_ns_name = '_api_v2_entity_sub'
@classmethod
def setUpTestData(self):
self.kwargs_item_create.update({
'entity_type': self.model._meta.model_name
})
super().setUpTestData()
def test_api_field_exists_entity_value(self):
""" Test for value of API Field
entity_type field must match model name
"""
assert self.api_data['entity_type'] == self.model._meta.model_name
class EntityAPITest(
APITestCases,
TestCase,
):
kwargs_item_create: dict = None
model = Entity
url_ns_name = '_api_v2_entity'
@classmethod
def setUpTestData(self):
self.kwargs_item_create = {
'entity_type': 'entity'
}
super().setUpTestData()

View File

@ -1,64 +1,274 @@
from django.test import TestCase
import pytest
from django.db import models
from access.models.entity import Entity
from access.models.organization import Organization
from app.tests.abstract.models import TenancyModel
from app.tests.unit.test_unit_models import (
PyTestTenancyObjectInheritedCases,
)
class ModelTestCases(
TenancyModel,
class EntityModelTestCases(
PyTestTenancyObjectInheritedCases,
):
model = Entity
base_model = Entity
kwargs_item_create: dict = {}
kwargs_create_item: dict = {}
@classmethod
def setUpTestData(self):
"""Setup Test"""
sub_model_type = 'entity'
"""Sub Model Type
sub-models must have this attribute defined in `ModelName.Meta.sub_model_type`
"""
self.organization = Organization.objects.create(name='test_org')
different_organization = Organization.objects.create(name='test_different_organization')
parameterized_fields: dict = {
"entity_type": {
'field_type': models.fields.CharField,
'field_parameter_default_exists': False,
# 'field_parameter_default_value': 'entity',
'field_parameter_verbose_name_type': str
},
# "asset_number": {
# 'field_type': models.fields.CharField,
# 'field_parameter_default_exists': False,
# 'field_parameter_verbose_name_type': str,
# },
# "serial_number": {
# 'field_type': models.fields.CharField,
# 'field_parameter_default_exists': False,
# 'field_parameter_verbose_name_type': str,
# }
}
self.item = self.model.objects.create(
organization = self.organization,
model_notes = 'notes',
entity_type = self.model._meta.model_name,
**self.kwargs_item_create,
)
@pytest.fixture( scope = 'class')
def setup_model(self,
request,
model,
django_db_blocker,
organization_one,
organization_two
):
with django_db_blocker.unblock():
request.cls.organization = organization_one
request.cls.different_organization = organization_two
kwargs_create_item = {}
for base in reversed(request.cls.__mro__):
if hasattr(base, 'kwargs_create_item'):
if base.kwargs_create_item is None:
continue
kwargs_create_item.update(**base.kwargs_create_item)
if len(kwargs_create_item) > 0:
request.cls.kwargs_create_item = kwargs_create_item
if 'organization' not in request.cls.kwargs_create_item:
request.cls.kwargs_create_item.update({
'organization': request.cls.organization
})
yield
@pytest.fixture( scope = 'class', autouse = True)
def class_setup(self,
setup_model,
create_model,
):
pass
def test_class_inherits_entity(self):
""" Class inheritence
TenancyObject must inherit SaveHistory
"""
assert issubclass(self.model, Entity)
def test_attribute_type_history_app_label(self):
"""Attribute Type
history_app_label is of type str
"""
assert type(self.model.history_app_label) is str
def test_attribute_value_history_app_label(self):
"""Attribute Type
history_app_label has been set, override this test case with the value
of attribute `history_app_label`
"""
assert self.model.history_app_label == 'access'
def test_attribute_type_history_model_name(self):
"""Attribute Type
history_model_name is of type str
"""
assert type(self.model.history_model_name) is str
def test_attribute_value_history_model_name(self):
"""Attribute Type
history_model_name has been set, override this test case with the value
of attribute `history_model_name`
"""
assert self.model.history_model_name == 'entity'
def test_attribute_type_kb_model_name(self):
"""Attribute Type
kb_model_name is of type str
"""
assert type(self.model.kb_model_name) is str
def test_attribute_value_kb_model_name(self):
"""Attribute Type
kb_model_name has been set, override this test case with the value
of attribute `kb_model_name`
"""
assert self.model.kb_model_name == 'entity'
def test_attribute_type_note_basename(self):
"""Attribute Type
note_basename is of type str
"""
assert type(self.model.note_basename) is str
def test_attribute_value_note_basename(self):
"""Attribute Type
note_basename has been set, override this test case with the value
of attribute `note_basename`
"""
assert self.model.note_basename == '_api_v2_entity_note'
# def test_function_is_property_get_model_type(self):
# """Function test
# Confirm function `get_model_type` is a property
# """
# assert type(self.model.get_model_type) is property
# def test_function_value_get_model_type(self):
# """Function test
# Confirm function `get_model_type` does not have a value of None
# value should be equaul to Meta.sub_model_type
# """
# assert self.item.get_model_type == self.item._meta.sub_model_type
def test_function_value_get_related_model(self):
"""Function test
Confirm function `get_related_model` is of the sub-model type
"""
assert type(self.item.get_related_model()) == self.model
def test_function_value_get_url(self):
assert self.item.get_url() == '/api/v2/access/entity/' + str(self.item.id)
class EntityModelInheritedCases(
ModelTestCases,
EntityModelTestCases,
):
"""Sub-Entity Test Cases
"""Sub-Ticket Test Cases
Test Cases for Entity models that inherit from model Entity
Test Cases for Ticket models that inherit from model Entity
"""
kwargs_item_create: dict = None
kwargs_create_item: dict = {}
model = None
@classmethod
def setUpTestData(self):
sub_model_type = None
"""Ticket Sub Model Type
Ticket sub-models must have this attribute defined in `ModelNam.Meta.sub_model_type`
"""
self.kwargs_item_create.update(
super().kwargs_item_create
)
super().setUpTestData()
# def test_function_value_get_model_type(self):
# """Function test
# Confirm function `get_model_type` does not have a value of None
# value should be equaul to Meta.sub_model_type
# """
# assert self.item.get_model_type == self.item._meta.sub_model_type
class EntityModelTest(
ModelTestCases,
TestCase,
class EntityModelPyTest(
EntityModelTestCases,
):
pass
def test_function_value_get_related_model(self):
"""Function test
Confirm function `get_related_model` is None for base model
"""
assert self.item.get_related_model() is None

View File

@ -1,7 +1,9 @@
import django
import pytest
from unittest.mock import Mock, patch
from django.contrib.auth.models import User, Permission, AnonymousUser
from django.contrib.auth.models import Permission, AnonymousUser
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
@ -13,7 +15,7 @@ from api.viewsets.common import ModelViewSet
from access.mixins.organization import OrganizationMixin
from access.mixins.permissions import OrganizationPermissionMixin
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
@ -23,6 +25,8 @@ from core.models.manufacturer import Manufacturer
from settings.models.app_settings import AppSettings
User = django.contrib.auth.get_user_model()
class MyMockView(

View File

@ -1,18 +1,21 @@
import django
import pytest
import unittest
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, Permission, User
from django.contrib.auth.models import AnonymousUser, Permission
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import reverse
from django.test import Client, TestCase
from rest_framework.relations import Hyperlink
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
User = django.contrib.auth.get_user_model()

View File

@ -1,21 +1,24 @@
import django
import pytest
import unittest
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, Permission, User
from django.contrib.auth.models import AnonymousUser, Permission
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import reverse
from django.test import Client, TestCase
from rest_framework.relations import Hyperlink
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from api.tests.abstract.api_fields import APICommonFields
User = django.contrib.auth.get_user_model()
class OrganizationAPI(
TestCase,

View File

@ -1,6 +1,7 @@
# from django.conf import settings
import django
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, Permission, User
from django.contrib.auth.models import AnonymousUser, Permission
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import reverse
from django.test import TestCase, Client
@ -9,15 +10,17 @@ import pytest
import unittest
import requests
from access.models.organization import Organization
from access.models.tenant import Tenant
from access.models.team import Team
from access.models.team_user import TeamUsers
from access.tests.abstract.model_permissions_organization_manager import OrganizationManagerModelPermissionChange, OrganizationManagerModelPermissionView
from app.tests.abstract.model_permissions import ModelPermissionsView, ModelPermissionsChange
User = django.contrib.auth.get_user_model()
class OrganizationPermissions(
class TenantPermissions(
TestCase,
ModelPermissionsView,
ModelPermissionsChange,
@ -25,7 +28,7 @@ class OrganizationPermissions(
OrganizationManagerModelPermissionView,
):
model = Organization
model = Tenant
app_namespace = 'Access'
@ -50,11 +53,11 @@ class OrganizationPermissions(
4. create a user per team
"""
organization = Organization.objects.create(name='test_org')
organization = Tenant.objects.create(name='test_org')
self.organization = organization
different_organization = Organization.objects.create(
different_organization = Tenant.objects.create(
name='test_different_organization'
)

View File

@ -1,18 +1,21 @@
import django
import pytest
import unittest
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, Permission, User
from django.contrib.auth.models import AnonymousUser, Permission
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import reverse
from django.test import Client, TestCase
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from api.tests.abstract.api_permissions import APIPermissionChange, APIPermissionView
User = django.contrib.auth.get_user_model()
class OrganizationPermissionsAPI(TestCase, APIPermissionChange, APIPermissionView):

View File

@ -7,7 +7,7 @@ import unittest
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.models.team import Team

View File

@ -3,7 +3,7 @@ from django.test import TestCase
from core.tests.abstract.test_unit_model_history_api_v2 import PrimaryModelHistoryAPI
from access.models.organization_history import Organization, OrganizationHistory
from access.models.organization_history import Tenant as Organization, OrganizationHistory

View File

@ -1,36 +1,17 @@
from django.contrib.contenttypes.models import ContentType
# from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from core.tests.abstract.test_unit_model_notes_model import ModelNotesModel
from core.tests.unit.model_notes.test_unit_model_notes_model import (
ModelNotesInheritedCases
)
from access.models.organization_notes import OrganizationNotes
class OrganizationNotesModel(
ModelNotesModel,
ModelNotesInheritedCases,
TestCase,
):
model = OrganizationNotes
@classmethod
def setUpTestData(self):
"""Setup Test"""
super().setUpTestData()
self.item = self.model.objects.create(
organization = self.organization,
content = 'a random comment for an exiting item',
content_type = ContentType.objects.get(
app_label = str(self.model._meta.app_label).lower(),
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
),
model = self.model.model.field.related_model.objects.create(
name = 'note model existing item',
),
created_by = self.user,
)

View File

@ -0,0 +1,14 @@
import pytest
from access.models.person import Person
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Person
yield request.cls.model
del request.cls.model

View File

@ -1,32 +0,0 @@
from django.test import TestCase
from access.models.person import Person
from access.tests.unit.entity.test_unit_entity_access_tenancy_object import (
EntityTenancyObjectInheritedCases,
)
class TenancyObjectTestCases(
EntityTenancyObjectInheritedCases,
):
model = Person
class PersonTenancyObjectInheritedCases(
TenancyObjectTestCases,
):
"""Sub-Entity Test Cases
Test Cases for Entity models that inherit from model Person
"""
model = None
class PersonTenancyObjectTest(
TenancyObjectTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,50 @@
from access.tests.unit.entity.test_unit_entity_api_fields import (
EntityAPIInheritedCases
)
class PersonAPITestCases(
EntityAPIInheritedCases,
):
parameterized_test_data = {
'f_name': {
'expected': str
},
'm_name': {
'expected': str
},
'l_name': {
'expected': str
},
'dob': {
'expected': str
}
}
kwargs_create_item: dict = {
'entity_type': 'person',
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Funny',
'dob': '2025-04-08',
}
class PersonAPIInheritedCases(
PersonAPITestCases,
):
kwargs_create_item: dict = None
model = None
class PersonAPIPyTest(
PersonAPITestCases,
):
pass

View File

@ -1,127 +0,0 @@
from django.test import TestCase
from access.models.person import Person
from access.tests.unit.entity.test_unit_entity_api_v2 import (
EntityAPIInheritedCases,
)
class APITestCases(
EntityAPIInheritedCases,
):
model = Person
kwargs_item_create: dict = {}
url_ns_name = '_api_v2_entity_sub'
@classmethod
def setUpTestData(self):
self.kwargs_item_create.update({
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Funny',
'dob': '2025-04-08',
})
super().setUpTestData()
def test_api_field_exists_f_name(self):
""" Test for existance of API Field
f_name field must exist
"""
assert 'f_name' in self.api_data
def test_api_field_type_f_name(self):
""" Test for type for API Field
f_name field must be str
"""
assert type(self.api_data['f_name']) is str
def test_api_field_exists_m_name(self):
""" Test for existance of API Field
m_name field must exist
"""
assert 'm_name' in self.api_data
def test_api_field_type_f_name(self):
""" Test for type for API Field
m_name field must be str
"""
assert type(self.api_data['m_name']) is str
def test_api_field_exists_l_name(self):
""" Test for existance of API Field
l_name field must exist
"""
assert 'l_name' in self.api_data
def test_api_field_type_f_name(self):
""" Test for type for API Field
l_name field must be str
"""
assert type(self.api_data['l_name']) is str
def test_api_field_exists_dob(self):
""" Test for existance of API Field
dob field must exist
"""
assert 'dob' in self.api_data
def test_api_field_type_dob(self):
""" Test for type for API Field
dob field must be str
"""
assert type(self.api_data['dob']) is str
class PersonAPIInheritedCases(
APITestCases,
):
"""Sub-Entity Test Cases
Test Cases for Entity models that inherit from model Person
"""
kwargs_item_create: dict = None
model = None
class PersonAPITest(
APITestCases,
TestCase,
):
pass

View File

@ -1,5 +1,6 @@
from django.db.models.fields import NOT_PROVIDED
from django.test import TestCase
import pytest
from django.db import models
from access.models.person import Person
from access.tests.unit.entity.test_unit_entity_model import (
@ -8,88 +9,114 @@ from access.tests.unit.entity.test_unit_entity_model import (
class ModelTestCases(
class PersonModelTestCases(
EntityModelInheritedCases,
):
model = Person
kwargs_item_create: dict = {
kwargs_create_item: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Funny',
'dob': '2025-04-08',
}
sub_model_type = 'person'
"""Sub Model Type
sub-models must have this attribute defined in `ModelName.Meta.sub_model_type`
"""
def test_model_field_dob_optional(self):
"""Test Field
parameterized_fields: dict = {
"f_name": {
'field_type': models.fields.CharField,
'field_parameter_default_exists': False,
'field_parameter_verbose_name_type': str,
},
"m_name": {
'field_type': models.fields.CharField,
'field_parameter_default_exists': False,
'field_parameter_verbose_name_type': str,
},
"l_name": {
'field_type': models.fields.CharField,
'field_parameter_default_exists': False,
'field_parameter_verbose_name_type': str,
},
"dob": {
'field_type': models.fields.DateField,
'field_parameter_default_exists': False,
'field_parameter_verbose_name_type': str,
},
}
Field `dob` must be an optional field
def test_class_inherits_person(self):
""" Class inheritence
TenancyObject must inherit SaveHistory
"""
assert self.model._meta.get_field('dob').blank
assert issubclass(self.model, Person)
def test_model_field_f_name_mandatory(self):
"""Test Field
def test_attribute_value_history_app_label(self):
"""Attribute Type
Field `f_name` must be a mandatory field
history_app_label has been set, override this test case with the value
of attribute `history_app_label`
"""
assert(
not (
self.model._meta.get_field('f_name').blank
and self.model._meta.get_field('f_name').null
)
and self.model._meta.get_field('f_name').default is NOT_PROVIDED
)
assert self.model.history_app_label == 'access'
def test_model_field_l_name_mandatory(self):
"""Test Field
def test_attribute_value_history_model_name(self):
"""Attribute Type
Field `l_name` must be a mandatory field
history_model_name has been set, override this test case with the value
of attribute `history_model_name`
"""
assert (
not (
self.model._meta.get_field('l_name').blank
and self.model._meta.get_field('l_name').null
)
and self.model._meta.get_field('l_name').default is NOT_PROVIDED
)
assert self.model.history_model_name == 'person'
def test_function_value_get_url(self):
assert self.item.get_url() == '/api/v2/access/entity/person/' + str(self.item.id)
class PersonModelInheritedCases(
ModelTestCases,
PersonModelTestCases,
):
"""Sub-Entity Test Cases
"""Sub-Ticket Test Cases
Test Cases for Entity models that inherit from model Person
Test Cases for Ticket models that inherit from model Entity
"""
kwargs_item_create: dict = None
kwargs_create_item: dict = {}
model = None
@classmethod
def setUpTestData(self):
self.kwargs_item_create.update(
super().kwargs_item_create
)
super().setUpTestData()
sub_model_type = None
"""Ticket Sub Model Type
Ticket sub-models must have this attribute defined in `ModelNam.Meta.sub_model_type`
"""
class PersonModelTest(
ModelTestCases,
TestCase,
class PersonModelPyTest(
PersonModelTestCases,
):
pass
def test_function_value_get_related_model(self):
"""Function test
Confirm function `get_related_model` is None for base model
"""
assert self.item.get_related_model() is None

View File

@ -1,21 +0,0 @@
from django.test import TestCase
from access.models.role import Role
from access.tests.abstract.tenancy_object import TenancyObject
class TenancyObjectTestCases(
TenancyObject,
):
model = None
class RoleTenancyObjectTest(
TenancyObjectTestCases,
TestCase,
):
model = Role

View File

@ -1,4 +1,6 @@
from django.contrib.auth.models import Permission, User
import django
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import reverse
from django.test import Client, TestCase
@ -6,12 +8,14 @@ from django.test import Client, TestCase
# from rest_framework.relations import Hyperlink
from access.models.role import Role
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from api.tests.abstract.api_fields import APITenancyObject
User = django.contrib.auth.get_user_model()
class APITestCases(

View File

@ -1,38 +1,23 @@
from django.test import TestCase
from access.models.role import Role
from access.models.organization import Organization
from access.models.tenancy import TenancyObject
from app.tests.abstract.models import TenancyModel
from app.tests.unit.test_unit_models import (
TenancyObjectInheritedCases
)
class ModelTestCases(
TenancyModel,
class RoleModelTestCases(
TenancyObjectInheritedCases,
):
model = None
kwargs_item_create: dict = None
@classmethod
def setUpTestData(self):
"""Setup Test"""
self.organization = Organization.objects.create(name='test_org')
different_organization = Organization.objects.create(name='test_different_organization')
self.item = self.model.objects.create(
organization = self.organization,
model_notes = 'notes',
**self.kwargs_item_create,
)
def test_field_not_exists_is_global(self):
def test_field_exist_is_global(self):
"""Test model field not used
object must not be settable as a global object
@ -44,18 +29,8 @@ class ModelTestCases(
def test_model_must_be_by_organization(self):
"""Test model must be by organization
This model **must** be by organization.
"""
assert issubclass(self.model, TenancyObject)
class RoleModelTest(
ModelTestCases,
RoleModelTestCases,
TestCase,
):

Some files were not shown because too many files have changed in this diff Show More