Compare commits

...

44 Commits

Author SHA1 Message Date
4c41994068 build: bump version 1.0.0-b14 -> 1.0.0 2024-08-23 08:03:31 +00:00
Jon
6f7b3ffad6 chore: add github pr template
ref: #213
2024-08-20 14:21:00 +09:30
Jon
cc97128e25 ci: add mkdocs workflow
ref: #213
2024-08-20 12:28:43 +09:30
Jon
b6ba3d38dc docs: migrate project links to github
ref: #213
2024-08-20 12:08:59 +09:30
Jon
18b788844a ci: update gitlab-ci to current head
ref: #209
2024-08-19 16:53:31 +09:30
9d5464b5a9 build: bump version 1.0.0-b13 -> 1.0.0-b14 2024-08-12 06:02:07 +00:00
Jon
7848397ae2 Merge pull request #224 from nofusscomputing/223-fix-api-team-mnotes
CRUD operation for team notes
2024-08-12 15:29:21 +09:30
Jon
f298ce94bf test(access): test field model_notes
closes #223
2024-08-12 15:15:28 +09:30
Jon
3cace8943e fix(api): ensure model_notes is an available field
#223
2024-08-12 15:14:58 +09:30
Jon
aa40d68c88 build: add test to changelog
#209
2024-08-11 19:30:45 +09:30
c0f186db89 build: bump version 1.0.0-b12 -> 1.0.0-b13 2024-08-11 08:01:20 +00:00
Jon
6b35e7808c Merge pull request #222 from nofusscomputing/153-model-validations
fix: Audit models for validations
2024-08-11 17:21:20 +09:30
Jon
467f6fca6b fix(itam): Ensure device name is formatted according to RFC1035 2.3.1
see https://datatracker.ietf.org/doc/html/rfc1035#autoid-6

#222 closes #153
2024-08-11 17:09:06 +09:30
Jon
f86b2d5216 fix(itam): Ensure device UUID is correctly formatted
#153 #222
2024-08-11 17:08:56 +09:30
Jon
e29d8e1ec1 fix(config_management): Ensure that config group can't set self as parent
interface already filters self out, however check still to be done.

. #153 #222
2024-08-11 17:07:39 +09:30
Jon
0fc5f41391 fix(settings): ensure that the api token cant be saved to notes field
#153
2024-08-11 16:26:19 +09:30
Jon
4b29448d84 Merge pull request #220 from nofusscomputing/162-api-field-validtion
test: api field checks
2024-08-11 14:37:46 +09:30
Jon
e9fe4896df ci: mirror repo to gitlab
. #220 closes #214
2024-08-11 14:29:02 +09:30
Jon
b9d32a2c16 docs(tests): update testing docs explaining test types
#220 closes #162
2024-08-11 12:57:33 +09:30
Jon
d6bd99c5de docs: update project badges to reflect github hosted project
. #220
2024-08-11 12:38:20 +09:30
Jon
7de5ab12bf docs(readme): correct status badge icon to github
. #220
2024-08-11 12:37:52 +09:30
Jon
3fe09fb8f9 test(software): api field checks
. #162 #220
2024-08-11 12:27:57 +09:30
Jon
eb6b03f731 docs(development): added api field test note
. #220
2024-08-11 12:05:46 +09:30
40e3078a58 build: bump version 1.0.0-b11 -> 1.0.0-b12 2024-08-10 11:23:21 +00:00
Jon
4ba79c6ae9 Merge pull request #218 from nofusscomputing/162-api-field-validtion
test: api field checks

#128 #162
2024-08-10 20:51:51 +09:30
Jon
b5c31d81d3 fix(api): ensure org mixin is inherited by software view
. #218 fixes #219
2024-08-10 20:35:06 +09:30
Jon
c3b585d416 fix(base): correct project links to github
. #218
2024-08-10 20:24:43 +09:30
Jon
84d21f4af8 test(teams): api field checks
. #162 #218
2024-08-10 19:58:04 +09:30
Jon
262e431834 test(organization): api field checks
. #162
2024-08-10 19:39:28 +09:30
cde2562048 build: bump version 1.0.0-b10 -> 1.0.0-b11 2024-08-10 08:30:02 +00:00
Jon
67d853cf25 Merge pull request #215 from nofusscomputing/dependabot/pip/django-5.0.8
chore(deps): bump django from 5.0.7 to 5.0.8

#209
2024-08-10 17:54:55 +09:30
Jon
3ba6bb5b4b docs(readme): correct build badge
#209 #214
2024-08-10 17:35:53 +09:30
84d4f48c63 chore(deps): bump django from 5.0.7 to 5.0.8
Bumps [django](https://github.com/django/django) from 5.0.7 to 5.0.8.
- [Commits](https://github.com/django/django/compare/5.0.7...5.0.8)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-10 17:22:47 +09:30
Jon
5e8bebbeb1 build(python): update installed packages as part of the build
#209
2024-08-10 17:22:07 +09:30
Jon
b66a8644a0 Merge pull request #217 from nofusscomputing/ci-adjustments
ci: migration items
2024-08-10 15:03:45 +09:30
Jon
f0ae185fc5 docs(readme): fix version badges
#217 #214
2024-08-10 14:02:27 +09:30
Jon
43b7e413a6 ci(project): add issue/pr project triage
https://github.com/nofusscomputing/action_project/pull/1 https://github.com/nofusscomputing/centurion_erp/issues/214 #217
2024-08-10 13:45:58 +09:30
Jon
04dc00d79d ci(gitlab): fix includes
https://github.com/nofusscomputing/centurion_erp/issues/214
2024-08-10 13:35:37 +09:30
8e6fd58107 build: bump version 1.0.0-b9 -> 1.0.0-b10 2024-08-09 12:53:39 +00:00
Jon
bfe9a95038 ci: remove docker.io from image publish name
#209
2024-08-09 22:23:03 +09:30
33687791ec build: bump version 1.0.0-b8 -> 1.0.0-b9 2024-08-09 12:43:24 +00:00
Jon
57bc972b0f ci: use correct docker.io credentials
#209
2024-08-09 22:12:19 +09:30
Jon
f437eeccb8 ci: use full docker.io as publish image name
#209
2024-08-09 22:12:19 +09:30
Jon
4e11ad67d0 ci: use docker.io as publish registry
#209
2024-08-09 22:12:19 +09:30
26 changed files with 1645 additions and 72 deletions

View File

@ -1,8 +1,21 @@
---
commitizen:
name: cz_conventional_commits
customize:
change_type_map:
feature: Features
fix: Fixes
refactor: Refactoring
test: Tests
change_type_order:
- BREAKING CHANGE
- feat
- fix
- test
- refactor
commit_parser: ^(?P<change_type>feat|fix|test|refactor|perf|BREAKING CHANGE)(?:\((?P<scope>[^()\r\n]*)\)|\()?(?P<breaking>!)?:\s(?P<message>.*)?
name: cz_customize
prerelease_offset: 1
tag_format: $version
update_changelog_on_bump: false
version: 1.0.0-b8
version: 1.0.0
version_scheme: semver

View File

@ -10,20 +10,33 @@ on:
tags:
- '*'
env:
GIT_SYNC_URL: "https://${{ secrets.GITLAB_USERNAME_ROBOT }}:${{ secrets.GITLAB_TOKEN_ROBOT }}@gitlab.com/nofusscomputing/projects/centurion_erp.git"
jobs:
mkdocs:
name: 'MKDocs'
permissions:
pull-requests: write
contents: write
statuses: write
checks: write
actions: write
uses: nofusscomputing/action_mkdocs/.github/workflows/reusable_mkdocs.yaml@development
docker:
name: 'Docker'
uses: nofusscomputing/action_docker/.github/workflows/docker.yaml@development
with:
DOCKER_BUILD_IMAGE_NAME: "nofusscomputing/centurion-erp"
DOCKER_PUBLISH_REGISTRY: "docker.io/nofusscomputing"
DOCKER_PUBLISH_REGISTRY: "docker.io"
DOCKER_PUBLISH_IMAGE_NAME: "nofusscomputing/centurion-erp"
secrets:
DOCKER_PUBLISH_USERNAME: ${{ secrets.NFC_DOCKERHUB_TOKEN }}
DOCKER_PUBLISH_PASSWORD: ${{ secrets.NFC_DOCKERHUB_USERNAME }}
DOCKER_PUBLISH_USERNAME: ${{ secrets.NFC_DOCKERHUB_USERNAME }}
DOCKER_PUBLISH_PASSWORD: ${{ secrets.NFC_DOCKERHUB_TOKEN }}
python:
@ -31,3 +44,77 @@ jobs:
uses: nofusscomputing/action_python/.github/workflows/python.yaml@development
secrets:
WORKFLOW_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}
gitlab-mirror:
if: ${{ github.repository == 'nofusscomputing/centurion_erp' }}
runs-on: ubuntu-latest
steps:
- name: Checks
shell: bash
run: |
if [ "0${{ env.GIT_SYNC_URL }}" == "0" ]; then
echo "[ERROR] you must define variable GIT_SYNC_URL for mirroring this repository.";
exit 1;
fi
- name: clone
shell: bash
run: |
git clone --mirror https://github.com/${{ github.repository }} repo;
ls -la repo/
- name: add remote
shell: bash
run: |
cd repo;
echo "**************************************** - git remote -v";
git remote -v;
echo "****************************************";
git remote add destination $GIT_SYNC_URL;
- name: push branches
shell: bash
run: |
cd repo;
echo "**************************************** - git branch";
git branch;
echo "****************************************";
# git push destination --all --force;
git push destination --mirror || true;
# - name: push tags
# shell: bash
# run: |
# cd repo;
# echo "**************************************** - git tag";
# git tag;
# echo "****************************************";
# git push destination --tags --force;

37
.github/workflows/triage.yaml vendored Normal file
View File

@ -0,0 +1,37 @@
---
name: Triage
on:
issues:
types:
- opened
- reopened
- transferred
- milestoned
- demilestoned
- closed
- assigned
pull_request:
types:
- opened
- edited
- assigned
- reopened
- closed
jobs:
project:
name: Project
uses: nofusscomputing/action_project/.github/workflows/project.yaml@development
with:
PROJECT_URL: https://github.com/orgs/nofusscomputing/projects/3
secrets:
WORKFLOW_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}

View File

@ -32,9 +32,9 @@ include:
file:
- .gitlab-ci_common.yaml
# - template/automagic.gitlab-ci.yaml
- local: gitlab-ci/automation/.gitlab-ci-ansible.yaml
- local: gitlab-ci/template/mkdocs-documentation.gitlab-ci.yaml
- local: gitlab-ci/lint/ansible.gitlab-ci.yaml
- automation/.gitlab-ci-ansible.yaml
- template/mkdocs-documentation.gitlab-ci.yaml
- lint/ansible.gitlab-ci.yaml

View File

@ -1,3 +1,51 @@
## 1.0.0 (2024-08-23)
## 1.0.0-b14 (2024-08-12)
### Fixes
- **api**: ensure model_notes is an available field
### Tests
- **access**: test field model_notes
## 1.0.0-b13 (2024-08-11)
### Fixes
- Audit models for validations
- **itam**: Ensure device name is formatted according to RFC1035 2.3.1
- **itam**: Ensure device UUID is correctly formatted
- **config_management**: Ensure that config group can't set self as parent
- **settings**: ensure that the api token cant be saved to notes field
### Tests
- api field checks
- **software**: api field checks
## 1.0.0-b12 (2024-08-10)
### Fixes
- **api**: ensure org mixin is inherited by software view
- **base**: correct project links to github
### Tests
- api field checks
#128 #162
- **teams**: api field checks
- **organization**: api field checks
## 1.0.0-b11 (2024-08-10)
## 1.0.0-b10 (2024-08-09)
## 1.0.0-b9 (2024-08-09)
## 1.0.0-b8 (2024-08-09)
## 1.0.0-b7 (2024-08-09)
@ -6,62 +54,78 @@
## 1.0.0-b5 (2024-07-31)
### Feat
### feat
- add Config groups to API
- **api**: Add device config groups to devices
- **api**: Ability to fetch configgroups from api along with config
### Fix
### Fixes
- **api**: Ensure device groups is read only
### Tests
- **api**: Field existence and type checks for device
- **api**: test configgroups API fields
## 1.0.0-b4 (2024-07-29)
### Feat
### feat
- **swagger**: remove `{format}` suffixed doc entries
### Fix
### Fixes
- release-b3 fixes
- **api**: cleanup team post/get
- **api**: confirm HTTP method is allowed before permission check
- **api**: Ensure that organizations can't be created via the API
- **access**: Team model class inheritance order corrected
### Tests
- confirm that the tenancymanager is called
## 1.0.0-b3 (2024-07-21)
### Fix
### Fixes
- **itam**: Limit os version count to devices user has access to
## 1.0.0-b2 (2024-07-19)
### Fix
### Fixes
- **itam**: only show os version once
## 1.0.0-b1 (2024-07-19)
### Fix
### Fixes
- **itam**: ensure installed operating system count is limited to users organizations
- **itam**: ensure installed software count is limited to users organizations
## 1.0.0-a4 (2024-07-18)
### Feat
### feat
- **api**: When processing uploaded inventory and name does not match, update name to one within inventory file
- **config_management**: Group name to be entire breadcrumb
### Tests
- ensure inventory upload matches by both serial number and uuid if device name different
- placeholder for moving organization
## 1.0.0-a3 (2024-07-18)
### Feat
### feat
- **config_management**: Prevent a config group from being able to change organization
- **itam**: On device organization change remove config groups
### Fix
### Fixes
- **config_management**: dont attempt to do action during save if group being created
- **itam**: remove org filter for device so that user can see installations
@ -72,13 +136,13 @@
## 1.0.0-a2 (2024-07-17)
### Feat
### feat
- **api**: Inventory matching of device second by uuid
- **api**: Inventory matching of device first by serial number
- **base**: show warning bar if the user has not set a default organization
### Fix
### Fixes
- **base**: dont show user warning bar for non-authenticated user
- **api**: correct inventory operating system selection by name
@ -91,25 +155,31 @@
- squashed DB migrations in preparation for v1.0 release.
### Feat
### feat
- Administratively set global items org/is_global field now read-only
- **access**: Add multi-tennant manager
### Fix
### Fixes
- **core**: migrate manufacturer to use new form/view logic
- **settings**: correct the permission to view manufacturers
- **access**: Correct team form fields
- **config_management**: don't exclude parent from field, only self
### Refactor
### Refactoring
- repo preperation for v1.0.0-Alpha-1
- Squash database migrations
### Tests
- tenancy objects
- refactor to single abstract model for inclusion.
## 0.7.0 (2024-07-14)
### Feat
### feat
- **core**: Filter every form field if associated with an organization to users organizations only
- **core**: add var `template_name` to common view template for all views that require it
@ -124,13 +194,14 @@
- **ui**: add some navigation icons
- **itam**: update inventory status icon
- **itam**: ensure device software pagination links keep interface on software tab
- "Migrate inventory processing to background worker"
- **access**: enable non-organization django permission checks
- **settings**: Add celery task results index and view page
- **base**: Add background worker
- **itam**: Update Serial Number from inventory if present and Serial Number not set
- **itam**: Update UUID from inventory if present and UUID not set
### Fix
### Fixes
- **config_management**: Don't allow a config group to assign itself as its parent
- **config_management**: correct permission for deleting a host from config group
@ -167,11 +238,14 @@
- **itam**: show device model name instead of ID
- **api**: Ensure if serial number from inventory is `null` that it's not used
- **api**: ensure checked uuid and serial number is used for updating
- inventory
- **itam**: only remove device software when not found during inventory upload
- **itam**: only update software version if different
- existing device without uuid not updated when uploading an inventory
- Device Software tab pagination does not work
- **itam**: correct device software pagination
### Refactor
### Refactoring
- adjust views missing add/change form to now use forms
- add navigation menu expand arrows
@ -186,31 +260,57 @@
- **api**: migrate inventory processing to background worker
- **itam**: only perform actions on device inventory if DB matches inventory item
### Tests
- add test test_view_*_attribute_not_exists_fields for add and change views
- fix test_view_change_attribute_type_form_class to test if type class
- **views**: add test cases for model views
- Add Test case abstract classes to models
- **inventory**: add mocks?? for calling background worker
- **view**: view permission checks
- **inventory**: update tests for background worker changes
## 0.6.0 (2024-06-30)
### Feat
### feat
- user api token
- **api**: API token authentication
- **api**: abilty for user to create/delete api token
- **api**: create token model
### Fix
### Fixes
- **user_token**: conduct user check on token view access
- **itam**: use same form for edit and add
- **itam**: dont add field inventorydate if adding new item
- **api**: inventory upload requires sanitization
### Refactor
### Refactoring
- **settings**: use seperate change/view views
- **settings**: use form for user settings
- **tests**: move unit tests to unit test sub-directory
### Tests
- **token_auth**: test authentication method token
- more tests
- add .coveragerc to remove non-code files from coverage report
- Unit Tests TenancyObjects
- Test Cases for TenancyObjects
- tests for checking links from rendered templetes
- **core**: test cases for notes permissions
- **config_management**: config groups history permissions
- **api**: Majority of Inventory upload tests
- **access**: TenancyObject field tests
- **access**: remove skipped api tests for team users
## 0.5.0 (2024-06-17)
### Feat
### feat
- Setup Organization Managers
- **access**: add notes field to organization
- **access**: add organization manger
- **config_management**: Use breadcrumbs for child group name display
@ -218,6 +318,7 @@
- **itam**: add a status of "bad" for devices
- **itam**: paginate device software tab
- **itam**: status of device visible on device index page
- API Browser
- **core**: add skeleton http browser
- **core**: Add a notes field to manufacturer/ publisher
- **itam**: Add a notes field to software category
@ -234,24 +335,28 @@
- **itam**: add docs icon to devices page
- **config_management**: add docs icon to config groups page
- **base**: add dynamic docs icon
- config group software
- **models**: add property parent_object to models that have a parent
- **config_management**: add config group software to group history
- **itam**: render group software config within device rendered config
- **config_management**: assign software action to config group
- sso
- add configuration value 'SESSION_COOKIE_AGE'
- remove development SECRET_KEY and enforce checking for user configured one
- **base**: build CSRF trusted origins from configuration
- **base**: Enforceable SSO ONLY
- **base**: configurable SSO
### Fix
### Fixes
- **itam**: remove requirement that user needs change device to add notes
- **core**: dont attempt to access parent_object if 'None' during history save
- **config_management**: Add missing parent item getter to model
- **core**: overridden save within SaveHistory to use default attributes
- **access**: overridden save to use default attributes
- History does not delete when item deleted
- **core**: on object delete remove history entries
- inventory upload cant determin object organization
- **api**: ensure proper permission checking
- dont throw an exception during settings load for an item django already checks
- **core**: Add overrides for delete so delete history saved for items with parent model
@ -259,7 +364,7 @@
- **base**: remove social auth from nav menu
- **access**: add a team user permissions to use team organization
### Refactor
### Refactoring
- **access**: relocate permission check to own function
- **itam**: move device os tab to details tab
@ -273,14 +378,58 @@
- login to use base template
- adjust template block names
### Tests
- **access**: team user model permission check for organization manager
- **access**: team model permission check for organization manager
- **access**: organization model permission check for organization manager
- **access**: add test cases for model delete as organization manager
- **access**: add test cases for model addd as organization manager
- **access**: add test cases for model change as organization manager
- **access**: add test cases for model view as organization manager
- write some more
- **core**: skip invalid tests
- **itam**: tests for device type history entries
- **core**: tests for manufacturer history entries
- move manufacturer to it's parent
- refactor api model permission tests to use an abstract class of test cases
- move tests to the module they belong to
- refactor history permission tests to use an abstract class of test cases
- refactor model permission tests to use an abstract class of test cases
- refactor history entry to have test cases in abstract classes
- **itam**: history entry tests for software category
- **itam**: history entry tests for device operating system version
- **itam**: history entry tests for device operating system
- **itam**: history entry tests for device software
- **itam**: ensure child history is removed on config group software delete
- add placeholder tests
- **itam**: ensure history is removed on software delete
- **itam**: ensure history is removed on operating system delete
- **itam**: ensure history is removed on device model delete
- **config_management**: test history on delete for config groups
- **itam**: ensure history is removed on device delete
- **access**: test team history
- **access**: ensure team user history is created and removed as required
- **access**: ensure history is removed on team delete
- **access**: ensure history is removed on item delete
- **api**: Inventory upload permission checks
- **config_management**: testing of config_groups rendered config
- **config_management**: history save tests for config groups software
- **config_management**: config group software permission for add, change and delete
- **base**: placeholder tests for config groups software
- **base**: basic test for merge_software helper
- during unit tests add SECRET_KEY
## 0.4.0 (2024-06-05)
### Feat
### feat
- 2024 06 05
- **database**: add mysql support
- **api**: move invneotry api endpoint to '/api/device/inventory'
- **core**: support more history types
- **core**: function to fetch history entry item
- 2024 06 02
- **config_management**: Add button to groups ui for adding child group
- **access**: throw error if no organization added
- **itam**: add delete button to config group within ui
@ -293,10 +442,12 @@
- **api**: add swagger ui for documentation
- **api**: filter software to users organizations
- **api**: filter devices to users organizations
- randomz
- **api**: add org team view page
- API configuration of permissions
- **api**: configure team permissions
### Fix
### Fixes
- **itam**: ensure device type saves history
- **core**: correct history view permissions
@ -313,7 +464,7 @@
- **api**: correct reverse url lookup to use NS API
- **api**: permissions for organization
### Refactor
### Refactoring
- **access**: cache object so it doesnt have to be called multiple times
- **config_management**: move groups to nav menu
@ -321,20 +472,49 @@
- **api**: move permission check to mixin
- **access**: add team option to org permission check
### Tests
- **api**: placeholder test for inventory
- **settings**: access permission check for app settings
- **settings**: history view permission check for software category
- **settings**: history view permission check for manufacturer
- **settings**: history view permission check for device type
- **settings**: user settings
- **settings**: view permission check for user settings
- refactor core test layout
- **itam**: view permission check for software
- **itam**: view permission check for operating system
- **itam**: view permission check for device model
- **itam**: view permission check for device
- **config_management**: view permission check for config_groups
- **access**: view permission check for team
- **access**: view permission check for organization
- add history entry creation tests for most models
- **config_management**: when adding a host to config group filter out host that are already members of the group
- **config_management**: unit test for config groups model to ensure permissions are working
- **api**: remove tests for os and manufacturer as they are not used in api
- **api**: check model permissions for software
- **api**: check model permissions for devices
- **api**: check model permissions for teams
- **api**: check model permissions for organizations
## 0.3.0 (2024-05-29)
### Feat
### feat
- Randomz
- **access**: during organization permission check, check to ensure user is logged on
- **history**: always create an entry even if user=none
- **itam**: device uuid must be unique
- **itam**: device serial number must be unique
- 2024 05 26
- **setting**: Enable super admin to set ALL manufacturer/publishers as global
- **setting**: Enable super admin to set ALL device types as global
- **setting**: Enable super admin to set ALL device models as global
- **setting**: Enable super admin to set ALL software categories as global
- **UI**: show build details with page footer
- **software**: Add output to stdout to show what is and has occurred
- 2024 05 25
- **base**: Add delete icon to content header
- **itam**: Populate initial organization value from user default organization for software category creation
- **itam**: Populate initial organization value from user default organization for device type creation
@ -346,17 +526,20 @@
- Add management command software
- **setting**: Enable super admin to set ALL software as global
- **user**: Add user settings panel
- Manufacturer and Model Information
- **itam**: Add publisher to software
- **itam**: Add publisher to operating system
- **itam**: Add device model
- **core**: Add manufacturers
- **settings**: add dummy model for permissions
- **settings**: new module for whole of application settings/globals
- 2024 05 21-23
- **access**: Save changes to history for organization and teams
- **software**: Save changes to history
- **operating_system**: Save changes to history
- **device**: Save changes to history
- **core**: history model for saving model history
- 2024 05 19/20
- **itam**: Ability to add notes to software
- **itam**: Ability to add notes to operating systems
- **itam**: Ability to add notes on devices
@ -365,7 +548,7 @@
- **ui**: Show inventory details if they exist
- **api**: API accept computer inventory
### Fix
### Fixes
- **settings**: Add correct permissions for team user delete
- **settings**: Add correct permissions for team user view/change
@ -416,7 +599,7 @@
- correct typo in notes templates
- **ui**: Ensure navigation menu entry highlighted for sub items
### Refactor
### Refactoring
- **access**: add to models a get_organization function
- **access**: remove change view
@ -428,13 +611,33 @@
- **itam**: move device types to settings app
- **template**: content_title can be rendered in base
### Tests
- cleanup duplicate tests and minor reshuffle
- **access**: unit testing team user permissions
- **access**: unit testing team permissions
- **settings**: unit testing manufacturer permissions
- **settings**: unit testing software category permissions
- **device_model**: unit testing device type permissions
- **device_model**: unit testing device model permissions
- **organization**: unit testing organization permissions
- **operating_system**: unit testing operating system permissions
- **software**: unit testing software permissions
- **device**: unit testing device permissions
- adjust test layout and update contributing
- **core**: placeholder tests for history component
- **core**: place holder tests for notes model
- **api**: add placeholder tests for inventory
## 0.2.0 (2024-05-18)
### Feat
### feat
- 2024 05 18
- **itam**: Add Operating System to ITAM models
- **api**: force content type to be JSON for req/resp
- **software**: view software
- 2024 05 17
- **device**: Prevent devices from being set global
- **software**: if no installations found, denote
- **device**: configurable software version
@ -446,21 +649,24 @@
- **software**: add pagination for index
- **device**: add pagination for index
### Fix
### Fixes
- **device**: correct software link
## 0.1.0 (2024-05-17)
### Feat
### feat
- API token auth
- **api**: initial token authentication implementation
- itam and API setup
- **docker**: add settings to store data in separate volume
- **django**: add split settings for specifying additional settings paths
- **api**: Add device config to device
- **itam**: add organization to device installs
- **itam**: migrate app from own repo
- Enable API by default
- Genesis
- **admin**: remove team management
- **admin**: remove group management
- **access**: adjustable team permissions
@ -494,14 +700,14 @@
- **template**: add base template
- **django**: add organizations app
### Fix
### Fixes
- **itam**: device software to come from device org or global not users orgs
- **access**: correct team required permissions
- **fields**: correct autoslug field so it works
- **docker**: build wheels then install
### Refactor
### Refactoring
- button to use same selection colour
- **access**: remove inline form for org teams
@ -510,4 +716,8 @@
- **views**: move views to own directory
- **access**: addjust org and teams to use different view per action
### Tests
- interim unit tests
## 0.0.1 (2024-05-06)

View File

@ -4,7 +4,7 @@
<br>
![Project Status - Active](https://img.shields.io/badge/Project%20Status-Active-green?logo=gitlab&style=plastic)
![Project Status - Active](https://img.shields.io/badge/Project%20Status-Active-green?logo=github&style=plastic)
[![Docker Pulls](https://img.shields.io/docker/pulls/nofusscomputing/centurion-erp?style=plastic&logo=docker&color=0db7ed)](https://hub.docker.com/r/nofusscomputing/centurion-erp) [![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/centurion-erp)](https://artifacthub.io/packages/container/centurion-erp/centurion-erp)
@ -32,17 +32,18 @@ This project is hosted on [Github](https://github.com/NofussComputing/centurion_
**Stable Branch**
![GitHub branch status](https://img.shields.io/github/check-runs/nofusscomputing/centurion_erp/master?style=plastic&logo=github&label=Build&color=000) ![GitHub Release](https://img.shields.io/github/v/release/nofusscomputing/centurion_erp?sort=semver&display_name=release&style=plastic&logo=github&label=Build&color=000) ![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fnofusscomputing%2F.github%2Fmaster%2Frepositories%2Fnofusscomputing%2Fcenturion_erp%2Fmaster%2Fbadge_endpoint_coverage.json&style=plastic)
![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/nofusscomputing/centurion_erp/ci.yaml?branch=master&style=plastic&logo=github&label=Build&color=%23000) ![GitHub Release](https://img.shields.io/github/v/release/nofusscomputing/centurion_erp?sort=date&style=plastic&logo=github&label=Release&color=000) ![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fnofusscomputing%2F.github%2Fmaster%2Frepositories%2Fnofusscomputing%2Fcenturion_erp%2Fmaster%2Fbadge_endpoint_coverage.json&style=plastic)
![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fnofusscomputing%2F.github%2Fmaster%2Frepositories%2Fnofusscomputing%2Fcenturion_erp%2Fmaster%2Fbadge_endpoint_unit_test.json)
----
**Development Branch**
![GitHub branch status](https://img.shields.io/github/check-runs/nofusscomputing/centurion_erp/development?style=plastic&logo=github&label=Build&color=000) ![GitHub Release](https://img.shields.io/github/v/release/nofusscomputing/centurion_erp?include_prereleases&sort=semver&display_name=release&style=plastic&logo=github&label=Build&color=000) ![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fnofusscomputing%2F.github%2Fmaster%2Frepositories%2Fnofusscomputing%2Fcenturion_erp%2Fdevelopment%2Fbadge_endpoint_coverage.json&style=plastic)
![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/nofusscomputing/centurion_erp/ci.yaml?branch=development&style=plastic&logo=github&label=Build&color=%23000) ![GitHub Release](https://img.shields.io/github/v/release/nofusscomputing/centurion_erp?include_prereleases&sort=date&style=plastic&logo=github&label=Release&color=000) ![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fnofusscomputing%2F.github%2Fmaster%2Frepositories%2Fnofusscomputing%2Fcenturion_erp%2Fdevelopment%2Fbadge_endpoint_coverage.json&style=plastic)
![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fnofusscomputing%2F.github%2Fmaster%2Frepositories%2Fnofusscomputing%2Fcenturion_erp%2Fdevelopment%2Fbadge_endpoint_unit_test.json)

View File

@ -0,0 +1,371 @@
import pytest
import unittest
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, 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 import Organization, Team, TeamUsers, Permission
class OrganizationAPI(TestCase):
model = Organization
app_namespace = 'API'
url_name = '_api_organization'
@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 device
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
different_organization = Organization.objects.create(name='test_different_organization')
self.item = organization
self.url_view_kwargs = {'pk': self.item.id}
self.url_kwargs = {'pk': self.item.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])
self.view_user = User.objects.create_user(username="test_user_view", password="password")
teamuser = TeamUsers.objects.create(
team = view_team,
user = self.view_user
)
client = Client()
url = reverse(self.app_namespace + ':' + self.url_name, 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_id(self):
""" Test for existance of API Field
id field must exist
"""
assert 'id' in self.api_data
def test_api_field_type_id(self):
""" Test for type for API Field
id field must be int
"""
assert type(self.api_data['id']) is int
def test_api_field_exists_name(self):
""" Test for existance of API Field
name field must exist
"""
assert 'name' in self.api_data
def test_api_field_type_name(self):
""" Test for type for API Field
name field must be str
"""
assert type(self.api_data['name']) is str
def test_api_field_exists_teams(self):
""" Test for existance of API Field
teams field must exist
"""
assert 'teams' in self.api_data
def test_api_field_type_teams(self):
""" Test for type for API Field
teams field must be list
"""
assert type(self.api_data['teams']) is list
def test_api_field_exists_url(self):
""" Test for existance of API Field
url field must exist
"""
assert 'url' in self.api_data
def test_api_field_type_url(self):
""" Test for type for API Field
url field must be str
"""
assert type(self.api_data['url']) is Hyperlink
def test_api_field_exists_teams_id(self):
""" Test for existance of API Field
teams.id field must exist
"""
assert 'id' in self.api_data['teams'][0]
def test_api_field_type_teams_id(self):
""" Test for type for API Field
teams.id field must be int
"""
assert type(self.api_data['teams'][0]['id']) is int
def test_api_field_exists_teams_team_name(self):
""" Test for existance of API Field
teams.team_name field must exist
"""
assert 'team_name' in self.api_data['teams'][0]
def test_api_field_type_teams_team_name(self):
""" Test for type for API Field
teams.team_name field must be string
"""
assert type(self.api_data['teams'][0]['team_name']) is str
def test_api_field_exists_teams_permissions(self):
""" Test for existance of API Field
teams.permissions field must exist
"""
assert 'permissions' in self.api_data['teams'][0]
def test_api_field_type_teams_permissions(self):
""" Test for type for API Field
teams.permissions field must be list
"""
assert type(self.api_data['teams'][0]['permissions']) is list
def test_api_field_exists_teams_permissions_url(self):
""" Test for existance of API Field
teams.permissions_url field must exist
"""
assert 'permissions_url' in self.api_data['teams'][0]
def test_api_field_type_teams_permissions_url(self):
""" Test for type for API Field
teams.permissions_url field must be url
"""
assert type(self.api_data['teams'][0]['permissions_url']) is str
def test_api_field_exists_teams_url(self):
""" Test for existance of API Field
teams.url field must exist
"""
assert 'url' in self.api_data['teams'][0]
def test_api_field_type_teams_url(self):
""" Test for type for API Field
teams.url field must be url
"""
assert type(self.api_data['teams'][0]['url']) is str
def test_api_field_exists_teams_permissions_id(self):
""" Test for existance of API Field
teams.permissions.id field must exist
"""
assert 'id' in self.api_data['teams'][0]['permissions'][0]
def test_api_field_type_teams_permissions_id(self):
""" Test for type for API Field
teams.permissions.id field must be int
"""
assert type(self.api_data['teams'][0]['permissions'][0]['id']) is int
def test_api_field_exists_teams_permissions_name(self):
""" Test for existance of API Field
teams.permissions.name field must exist
"""
assert 'name' in self.api_data['teams'][0]['permissions'][0]
def test_api_field_type_teams_permissions_name(self):
""" Test for type for API Field
teams.permissions.name field must be str
"""
assert type(self.api_data['teams'][0]['permissions'][0]['name']) is str
def test_api_field_exists_teams_permissions_codename(self):
""" Test for existance of API Field
teams.permissions.codename field must exist
"""
assert 'codename' in self.api_data['teams'][0]['permissions'][0]
def test_api_field_type_teams_permissions_codename(self):
""" Test for type for API Field
teams.permissions.codename field must be str
"""
assert type(self.api_data['teams'][0]['permissions'][0]['codename']) is str
def test_api_field_exists_teams_permissions_content_type(self):
""" Test for existance of API Field
teams.permissions.content_type field must exist
"""
assert 'content_type' in self.api_data['teams'][0]['permissions'][0]
def test_api_field_type_teams_permissions_content_type(self):
""" Test for type for API Field
teams.permissions.content_type field must be dict
"""
assert type(self.api_data['teams'][0]['permissions'][0]['content_type']) is dict
def test_api_field_exists_teams_permissions_content_type_id(self):
""" Test for existance of API Field
teams.permissions.content_type.id field must exist
"""
assert 'id' in self.api_data['teams'][0]['permissions'][0]['content_type']
def test_api_field_type_teams_permissions_content_type_id(self):
""" Test for type for API Field
teams.permissions.content_type.id field must be int
"""
assert type(self.api_data['teams'][0]['permissions'][0]['content_type']['id']) is int
def test_api_field_exists_teams_permissions_content_type_app_label(self):
""" Test for existance of API Field
teams.permissions.content_type.app_label field must exist
"""
assert 'app_label' in self.api_data['teams'][0]['permissions'][0]['content_type']
def test_api_field_type_teams_permissions_content_type_app_label(self):
""" Test for type for API Field
teams.permissions.content_type.app_label field must be str
"""
assert type(self.api_data['teams'][0]['permissions'][0]['content_type']['app_label']) is str
def test_api_field_exists_teams_permissions_content_type_model(self):
""" Test for existance of API Field
teams.permissions.content_type.model field must exist
"""
assert 'model' in self.api_data['teams'][0]['permissions'][0]['content_type']
def test_api_field_type_teams_permissions_content_type_model(self):
""" Test for type for API Field
teams.permissions.content_type.model field must be str
"""
assert type(self.api_data['teams'][0]['permissions'][0]['content_type']['model']) is str

View File

@ -0,0 +1,313 @@
import pytest
import unittest
import requests
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, 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 import Organization, Team, TeamUsers, Permission
# from api.tests.abstract.api_permissions import APIPermissions
class TeamAPI(TestCase):
model = Team
app_namespace = 'API'
url_name = '_api_team'
# url_list = '_api_organization_teams'
# change_data = {'name': 'device'}
# delete_data = {'device': 'device'}
@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
different_organization = Organization.objects.create(name='test_different_organization')
self.item = self.model.objects.create(
organization=organization,
team_name = 'teamone',
model_notes = 'random note'
)
self.url_kwargs = {'organization_id': self.organization.id}
self.url_view_kwargs = {'organization_id': self.organization.id, 'group_ptr_id': self.item.id}
self.add_data = {'team_name': 'team_post'}
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,
# )
self.item.permissions.set([view_permissions])
self.view_user = User.objects.create_user(username="test_user_view", password="password")
teamuser = TeamUsers.objects.create(
team = self.item,
user = self.view_user
)
client = Client()
url = reverse(self.app_namespace + ':' + self.url_name, 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_id(self):
""" Test for existance of API Field
id field must exist
"""
assert 'id' in self.api_data
def test_api_field_type_id(self):
""" Test for type for API Field
id field must be int
"""
assert type(self.api_data['id']) is int
def test_api_field_exists_team_name(self):
""" Test for existance of API Field
team_name field must exist
"""
assert 'team_name' in self.api_data
def test_api_field_type_name(self):
""" Test for type for API Field
team_name field must be str
"""
assert type(self.api_data['team_name']) is str
def test_api_field_exists_model_notes(self):
""" Test for existance of API Field
model_notes field must exist
"""
assert 'model_notes' in self.api_data
def test_api_field_type_model_notes(self):
""" Test for type for API Field
model_notes field must be str
"""
assert type(self.api_data['model_notes']) is str
def test_api_field_exists_url(self):
""" Test for existance of API Field
url field must exist
"""
assert 'url' in self.api_data
def test_api_field_type_url(self):
""" Test for type for API Field
url field must be str
"""
assert type(self.api_data['url']) is str
def test_api_field_exists_permissions(self):
""" Test for existance of API Field
permissions field must exist
"""
assert 'permissions' in self.api_data
def test_api_field_type_permissions(self):
""" Test for type for API Field
url field must be list
"""
assert type(self.api_data['permissions']) is list
def test_api_field_exists_permissions_id(self):
""" Test for existance of API Field
permissions.id field must exist
"""
assert 'id' in self.api_data['permissions'][0]
def test_api_field_type_permissions_id(self):
""" Test for type for API Field
permissions.id field must be int
"""
assert type(self.api_data['permissions'][0]['id']) is int
def test_api_field_exists_permissions_name(self):
""" Test for existance of API Field
permissions.name field must exist
"""
assert 'name' in self.api_data['permissions'][0]
def test_api_field_type_permissions_name(self):
""" Test for type for API Field
permissions.name field must be str
"""
assert type(self.api_data['permissions'][0]['name']) is str
def test_api_field_exists_permissions_codename(self):
""" Test for existance of API Field
permissions.codename field must exist
"""
assert 'codename' in self.api_data['permissions'][0]
def test_api_field_type_permissions_codename(self):
""" Test for type for API Field
permissions.codename field must be str
"""
assert type(self.api_data['permissions'][0]['codename']) is str
def test_api_field_exists_permissions_content_type(self):
""" Test for existance of API Field
permissions.content_type field must exist
"""
assert 'content_type' in self.api_data['permissions'][0]
def test_api_field_type_permissions_content_type(self):
""" Test for type for API Field
permissions.content_type field must be dict
"""
assert type(self.api_data['permissions'][0]['content_type']) is dict
def test_api_field_exists_permissions_content_type_id(self):
""" Test for existance of API Field
permissions.content_type.id field must exist
"""
assert 'id' in self.api_data['permissions'][0]['content_type']
def test_api_field_type_permissions_content_type_id(self):
""" Test for type for API Field
permissions.content_type.id field must be int
"""
assert type(self.api_data['permissions'][0]['content_type']['id']) is int
def test_api_field_exists_permissions_content_type_app_label(self):
""" Test for existance of API Field
permissions.content_type.app_label field must exist
"""
assert 'app_label' in self.api_data['permissions'][0]['content_type']
def test_api_field_type_permissions_content_type_app_label(self):
""" Test for type for API Field
permissions.content_type.app_label field must be str
"""
assert type(self.api_data['permissions'][0]['content_type']['app_label']) is str
def test_api_field_exists_permissions_content_type_model(self):
""" Test for existance of API Field
permissions.content_type.model field must exist
"""
assert 'model' in self.api_data['permissions'][0]['content_type']
def test_api_field_type_permissions_content_type_model(self):
""" Test for type for API Field
permissions.content_type.model field must be str
"""
assert type(self.api_data['permissions'][0]['content_type']['model']) is str

View File

@ -5,6 +5,8 @@ import string
from django.conf import settings
from django.contrib.auth.models import User
from django.db import models
from django.db.models import Field
from django.forms import ValidationError
from access.fields import *
from access.models import TenancyObject
@ -14,6 +16,37 @@ from access.models import TenancyObject
class AuthToken(models.Model):
def validate_note_no_token(self, note, token):
""" Ensure plaintext token cant be saved to notes field.
called from app.settings.views.user_settings.TokenAdd.form_valid()
Args:
note (Field): _Note field_
token (Field): _Token field_
Raises:
ValidationError: _Validation failed_
"""
validation: bool = True
if str(note) == str(token):
validation = False
if str(token)[:9] in str(note): # Allow user to use up to 8 chars so they can reference it.
validation = False
if not validation:
raise ValidationError('Token can not be placed in the notes field.')
id = models.AutoField(
primary_key=True,
unique=True,

View File

@ -15,6 +15,7 @@ class TeamSerializerBase(serializers.ModelSerializer):
model = Team
fields = (
'team_name',
'model_notes',
'permissions',
'url',
)
@ -75,6 +76,7 @@ class TeamSerializer(TeamSerializerBase):
fields = (
"id",
"team_name",
'model_notes',
'permissions',
'permissions_url',
'url',

View File

@ -3,6 +3,8 @@ from django.shortcuts import get_object_or_404
from rest_framework import generics, viewsets
from access.mixin import OrganizationMixin
from api.serializers.itam.software import SoftwareSerializer
from api.views.mixin import OrganizationPermissionAPI
@ -10,7 +12,7 @@ from itam.models.software import Software
class SoftwareViewSet(viewsets.ModelViewSet):
class SoftwareViewSet(OrganizationMixin, viewsets.ModelViewSet):
permission_classes = [
OrganizationPermissionAPI

View File

@ -195,6 +195,12 @@ class ConfigGroups(GroupsCommonFields, SaveHistory):
# Prevent organization change. ToDo: add feature so that config can change organizations
self.organization = obj.organization
if self.parent is not None:
if self.pk == self.parent.pk:
raise ValidationError('Can not set self as parent')
super().save(*args, **kwargs)

View File

@ -1,8 +1,10 @@
import json
import re
from datetime import timedelta
from django.db import models
from django.forms import ValidationError
from access.fields import *
from access.models import TenancyObject
@ -18,6 +20,8 @@ from itam.models.operating_system import OperatingSystemVersion
from settings.models.app_settings import AppSettings
class DeviceType(DeviceCommonFieldsName, SaveHistory):
@ -39,6 +43,35 @@ class DeviceType(DeviceCommonFieldsName, SaveHistory):
class Device(DeviceCommonFieldsName, SaveHistory):
def validate_uuid_format(self):
pattern = r'[0-9|a-f]{8}\-[0-9|a-f]{4}\-[0-9|a-f]{4}\-[0-9|a-f]{4}\-[0-9|a-f]{12}'
if not re.match(pattern, str(self)):
raise ValidationError(f'UUID Must be in {str(pattern)}')
def validate_hostname_format(self):
pattern = r'^[a-z]{1}[a-z|0-9|\-]+[a-z|0-9]{1}$'
if not re.match(pattern, str(self).lower()):
raise ValidationError(
'''[RFC1035 2.3.1] A hostname must start with a letter, end with a letter or digit,
and have as interior characters only letters, digits, and hyphen.'''
)
name = models.CharField(
blank = False,
max_length = 50,
unique = True,
validators = [ validate_hostname_format ]
)
serial_number = models.CharField(
verbose_name = 'Serial Number',
max_length = 50,
@ -58,6 +91,7 @@ class Device(DeviceCommonFieldsName, SaveHistory):
blank = True,
unique = True,
help_text = 'System GUID/UUID.',
validators = [ validate_uuid_format ]
)
device_model = models.ForeignKey(

View File

@ -0,0 +1,294 @@
import pytest
import unittest
import requests
from django.contrib.auth.models import AnonymousUser, 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 import Organization, Team, TeamUsers, Permission
from core.models.manufacturer import Manufacturer
from itam.models.software import Software, SoftwareCategory
class SoftwareAPI(TestCase):
model = Software
app_namespace = 'API'
url_name = 'software-detail'
@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 software
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
different_organization = Organization.objects.create(name='test_different_organization')
category = SoftwareCategory.objects.create(
name='a category'
)
publisher = Manufacturer.objects.create(
name='a manufacturer'
)
self.item = self.model.objects.create(
organization=organization,
name = 'softwareone',
model_notes = 'random str',
category = category,
publisher = publisher
)
self.url_view_kwargs = {'pk': self.item.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])
self.view_user = User.objects.create_user(username="test_user_view", password="password")
teamuser = TeamUsers.objects.create(
team = view_team,
user = self.view_user
)
client = Client()
url = reverse(self.app_namespace + ':' + self.url_name, 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_id(self):
""" Test for existance of API Field
id field must exist
"""
assert 'id' in self.api_data
def test_api_field_type_id(self):
""" Test for type for API Field
id field must be int
"""
assert type(self.api_data['id']) is int
def test_api_field_exists_url(self):
""" Test for existance of API Field
url field must exist
"""
assert 'url' in self.api_data
def test_api_field_type_url(self):
""" Test for type for API Field
url field must be str
"""
assert type(self.api_data['url']) is Hyperlink
def test_api_field_exists_is_global(self):
""" Test for existance of API Field
is_global field must exist
"""
assert 'is_global' in self.api_data
def test_api_field_type_is_global(self):
""" Test for type for API Field
is_global field must be boolean
"""
assert type(self.api_data['is_global']) is bool
def test_api_field_exists_model_notes(self):
""" Test for existance of API Field
model_notes field must exist
"""
assert 'model_notes' in self.api_data
def test_api_field_type_model_notes(self):
""" Test for type for API Field
model_notes field must be str
"""
assert type(self.api_data['model_notes']) is str
def test_api_field_exists_name(self):
""" Test for existance of API Field
name field must exist
"""
assert 'name' in self.api_data
def test_api_field_type_name(self):
""" Test for type for API Field
name field must be str
"""
assert type(self.api_data['name']) is str
def test_api_field_exists_slug(self):
""" Test for existance of API Field
slug field must exist
"""
assert 'slug' in self.api_data
def test_api_field_type_slug(self):
""" Test for type for API Field
slug field must be str
"""
assert type(self.api_data['slug']) is str
def test_api_field_exists_created(self):
""" Test for existance of API Field
created field must exist
"""
assert 'created' in self.api_data
def test_api_field_type_created(self):
""" Test for type for API Field
created field must be str
"""
assert type(self.api_data['created']) is str
def test_api_field_exists_modified(self):
""" Test for existance of API Field
modified field must exist
"""
assert 'modified' in self.api_data
def test_api_field_type_modified(self):
""" Test for type for API Field
modified field must be str
"""
assert type(self.api_data['modified']) is str
def test_api_field_exists_organization(self):
""" Test for existance of API Field
organization field must exist
"""
assert 'organization' in self.api_data
def test_api_field_type_organization(self):
""" Test for type for API Field
organization field must be intt
"""
assert type(self.api_data['organization']) is int
def test_api_field_exists_publisher(self):
""" Test for existance of API Field
publisher field must exist
"""
assert 'publisher' in self.api_data
def test_api_field_type_publisher(self):
""" Test for type for API Field
publisher field must be int
"""
assert type(self.api_data['publisher']) is int
def test_api_field_exists_category(self):
""" Test for existance of API Field
category field must exist
"""
assert 'category' in self.api_data
def test_api_field_type_category(self):
""" Test for type for API Field
category field must be int
"""
assert type(self.api_data['category']) is int

View File

@ -101,6 +101,8 @@ class TokenAdd(AddView):
form.instance.user = self.request.user
form.instance.token = form.instance.token_hash(form.fields['gen_token'].initial)
self.model.validate_note_no_token(self, note=form.instance.note, token=form.fields['gen_token'].initial)
return super().form_valid(form)

View File

@ -133,7 +133,7 @@ section h2 span svg {
<a title="Swagger API Documentation" href="/api/swagger/" target="_blank">
{% include 'icons/swagger_docs.svg' %}
</a>
<a title="Code Home" href="https://gitlab.com/nofusscomputing/projects/centurion_erp" target="_blank">
<a title="Code Home" href="{{ build_details.project_url }}" target="_blank">
{% include 'icons/git.svg' %}
</a>
</span>
@ -147,7 +147,7 @@ section h2 span svg {
{% else %}
development
{% endif %}
( {% if build_details.project_url %}<a href="{{ build_details.project_url }}/-/commit/{{ build_details.sha }}" target="_blank">{% endif %}
( {% if build_details.project_url %}<a href="{{ build_details.project_url }}/commit/{{ build_details.sha }}" target="_blank">{% endif %}
{{ build_details.sha }}
{% if build_details.project_url %}</a>{% endif %} )
</span>

View File

@ -2,5 +2,5 @@
<span class="icon-issue">
<svg xmlns="http://www.w3.org/2000/svg" height="25px" viewBox="0 -960 960 960" width="25px"><path d="M480-144q-60 0-109-32.5T302-264h-74q-15.3 0-25.65-10.29Q192-284.58 192-299.79t10.35-25.71Q212.7-336 228-336h60v-60h-60q-15.3 0-25.65-10.29Q192-416.58 192-431.79t10.35-25.71Q212.7-468 228-468h60v-60h-60q-15.3 0-25.65-10.29Q192-548.58 192-563.79t10.35-25.71Q212.7-600 228-600h74q8-26 25.8-47.09Q345.6-668.18 369-684l-56-56q-11-11-10.5-25.5T314-791q11-11 25-11t25 11l76 75q19.86-5 40.43-5t40.57 5l75-75q11-11 25.67-11 14.66 0 25.33 11 11 11 11 25.5T647-740l-56 56q23 16 40 37t27 47h74q15.3 0 25.65 10.29Q768-579.42 768-564.21t-10.35 25.71Q747.3-528 732-528h-60v60h60q15.3 0 25.65 10.29Q768-447.42 768-432.21t-10.35 25.71Q747.3-396 732-396h-60v60h60q15.3 0 25.65 10.29Q768-315.42 768-300.21t-10.35 25.71Q747.3-264 732-264h-74q-20 55-69 87.5T480-144Zm0-72q48.67 0 83.34-35Q598-286 600-336v-192q2-50-33.5-85t-86-35q-50.5 0-85 35T360-528v192q-1 50 34 85t86 35Zm-36.09-120h71.83q15.26 0 25.76-10.29 10.5-10.29 10.5-25.5t-10.32-25.71Q531.35-408 516.09-408h-71.83q-15.26 0-25.76 10.29-10.5 10.29-10.5 25.5t10.32 25.71q10.33 10.5 25.59 10.5Zm0-120h71.83q15.26 0 25.76-10.29 10.5-10.29 10.5-25.5t-10.32-25.71Q531.35-528 516.09-528h-71.83q-15.26 0-25.76 10.29-10.5 10.29-10.5 25.5t10.32 25.71q10.33 10.5 25.59 10.5ZM480-430Z"/></svg>
</span>
<a href="https://gitlab.com/nofusscomputing/projects/django_template/-/issues/{{ issue }}" target="_blank"> see #{{ issue }}</a>
<a href="https://github.com/nofusscomputing/centurion_erp/issues/{{ issue }}" target="_blank"> see #{{ issue }}</a>
</span>

View File

@ -5,6 +5,11 @@ ARG CI_COMMIT_TAG=''
FROM python:3.11-alpine3.19 as build
RUN pip --disable-pip-version-check list --outdated --format=json | \
python -c "import json, sys; print('\n'.join([x['name'] for x in json.load(sys.stdin)]))" | \
xargs -n1 pip install --no-cache-dir -U;
RUN apk add --update \
bash \
git \
@ -83,7 +88,10 @@ COPY --from=build /tmp/python_builds /tmp/python_builds
COPY includes/ /
RUN apk update --no-cache; \
RUN pip --disable-pip-version-check list --outdated --format=json | \
python -c "import json, sys; print('\n'.join([x['name'] for x in json.load(sys.stdin)]))" | \
xargs -n1 pip install --no-cache-dir -U; \
apk update --no-cache; \
apk add --no-cache \
mariadb-client \
mariadb-dev \

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

View File

@ -0,0 +1,94 @@
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" version="24.7.7">
<diagram name="Page-1" id="0p1o9D85wg9-GzEIQXPA">
<mxGraphModel dx="2049" dy="1094" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="RO_ATfNDEhmNTfwGVFBN-24" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#E0E0E0;" vertex="1" parent="1">
<mxGeometry x="170" y="140" width="990" height="890" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-2" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="5" width="1155" height="80" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-3" value="Navigation" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="5" y="80" width="165" height="950" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-4" value="&lt;font style=&quot;font-size: 22px;&quot;&gt;&amp;lt;Type&amp;gt; &amp;lt;Title&amp;gt;&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
<mxGeometry x="170" y="80" width="990" height="60" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-5" value="Metadata ??" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="1010" y="150" width="150" height="660" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-6" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="180" y="150" width="820" height="150" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-7" value="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.&lt;br&gt;Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat&amp;nbsp;" style="text;spacingTop=-5;whiteSpace=wrap;html=1;align=left;fontSize=12;fontFamily=Helvetica;fillColor=none;strokeColor=none;" vertex="1" parent="1">
<mxGeometry x="190" y="170" width="800" height="160" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-8" value="Related" style="swimlane;strokeColor=#999999;swimlaneFillColor=#FFFFFF;fillColor=#ffffff;fontColor=#008CFF;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=0;marginBottom=0;whiteSpace=wrap;html=1;fontSize=17;align=left;" vertex="1" parent="1">
<mxGeometry x="180" y="310" width="770" height="100" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-9" value="Item 1" style="text;spacing=0;strokeColor=none;align=left;verticalAlign=middle;spacingLeft=7;spacingRight=10;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fillColor=none;fontColor=#666666;fontSize=17;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-8">
<mxGeometry y="30" width="770" height="20" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-10" value="Item 2" style="text;spacing=0;strokeColor=none;align=left;verticalAlign=middle;spacingLeft=7;spacingRight=10;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;rSize=5;fillColor=none;fontColor=#666666;fontSize=17;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-8">
<mxGeometry y="50" width="770" height="20" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-11" value="Item 3" style="text;spacing=0;strokeColor=none;align=left;verticalAlign=middle;spacingLeft=7;spacingRight=10;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;rSize=5;fillColor=#DDEEFF;fontColor=#666666;fontSize=17;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-8">
<mxGeometry y="70" width="770" height="20" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-13" value="" style="text;spacing=0;strokeColor=none;align=left;verticalAlign=middle;spacingLeft=7;spacingRight=10;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fillColor=none;fontColor=#666666;fontSize=17;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-8">
<mxGeometry y="90" width="770" height="10" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-15" value="Linked Items" style="swimlane;strokeColor=#999999;swimlaneFillColor=#FFFFFF;fillColor=#ffffff;fontColor=#008CFF;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=0;marginBottom=0;whiteSpace=wrap;html=1;fontSize=17;align=left;" vertex="1" parent="1">
<mxGeometry x="180" y="420" width="770" height="100" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-16" value="Item 1" style="text;spacing=0;strokeColor=none;align=left;verticalAlign=middle;spacingLeft=7;spacingRight=10;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fillColor=none;fontColor=#666666;fontSize=17;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-15">
<mxGeometry y="30" width="770" height="20" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-17" value="Item 2" style="text;spacing=0;strokeColor=none;align=left;verticalAlign=middle;spacingLeft=7;spacingRight=10;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;rSize=5;fillColor=none;fontColor=#666666;fontSize=17;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-15">
<mxGeometry y="50" width="770" height="20" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-18" value="Item 3" style="text;spacing=0;strokeColor=none;align=left;verticalAlign=middle;spacingLeft=7;spacingRight=10;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;rSize=5;fillColor=#DDEEFF;fontColor=#666666;fontSize=17;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-15">
<mxGeometry y="70" width="770" height="20" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-19" value="" style="text;spacing=0;strokeColor=none;align=left;verticalAlign=middle;spacingLeft=7;spacingRight=10;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fillColor=none;fontColor=#666666;fontSize=17;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-15">
<mxGeometry y="90" width="770" height="10" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-20" value="xx added ticket X as blocking" style="verticalLabelPosition=bottom;shadow=0;dashed=0;align=left;html=1;verticalAlign=top;strokeWidth=1;shape=mxgraph.mockup.graphics.simpleIcon;strokeColor=#999999;spacing=-20;spacingRight=0;spacingLeft=50;" vertex="1" parent="1">
<mxGeometry x="200" y="540" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-21" value="xx added Device as related" style="verticalLabelPosition=bottom;shadow=0;dashed=0;align=left;html=1;verticalAlign=top;strokeWidth=1;shape=mxgraph.mockup.graphics.simpleIcon;strokeColor=#999999;spacing=-20;spacingRight=0;spacingLeft=50;" vertex="1" parent="1">
<mxGeometry x="200" y="580" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-22" value="" style="verticalLabelPosition=bottom;shadow=0;dashed=0;align=center;html=1;verticalAlign=top;strokeWidth=1;shape=mxgraph.mockup.navigation.scrollBar;strokeColor=#999999;barPos=20;fillColor2=#99ddff;strokeColor2=none;direction=north;" vertex="1" parent="1">
<mxGeometry x="980" y="310" width="20" height="720" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-32" value="" style="group" vertex="1" connectable="0" parent="1">
<mxGeometry x="200" y="620" width="750" height="120" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-29" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-32">
<mxGeometry width="750" height="120" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-30" value="XX replied on xx month year" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-32">
<mxGeometry width="750" height="20" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-28" value="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.&lt;div&gt;&lt;br/&gt;&lt;/div&gt;" style="text;spacingTop=-5;whiteSpace=wrap;html=1;align=left;fontSize=12;fontFamily=Helvetica;fillColor=none;strokeColor=none;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-32">
<mxGeometry x="10" y="30" width="730" height="77" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-33" value="" style="group" vertex="1" connectable="0" parent="1">
<mxGeometry x="230" y="740" width="720" height="120" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-34" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-33">
<mxGeometry width="720" height="120" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-35" value="XX replied on xx month year" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-33">
<mxGeometry width="720" height="20" as="geometry" />
</mxCell>
<mxCell id="RO_ATfNDEhmNTfwGVFBN-36" value="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.&lt;div&gt;&lt;br/&gt;&lt;/div&gt;" style="text;spacingTop=-5;whiteSpace=wrap;html=1;align=left;fontSize=12;fontFamily=Helvetica;fillColor=none;strokeColor=none;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-33">
<mxGeometry x="9.6" y="30" width="700.8" height="77" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@ -8,14 +8,18 @@ about: https://gitlab.com/nofusscomputing/infrastructure/configuration-managemen
Unit tests are written to aid in application stability and to assist in preventing regression bugs. As part of development the developer working on a Merge/Pull request is to ensure that tests are written. Failing to do so will more likely than not ensure that your Merge/Pull request is not merged.
User Interface (UI) test are written to test the user interface to ensure that it functions as it should. Changes to the UI will need to be tested.
User Interface (UI) test are written _if applicable_ to test the user interface to ensure that it functions as it should. Changes to the UI will need to be tested.
In most cases functional tests will not need to be written, however you should confirm this with a maintainer.
Integration tests **will** be required if the development introduces code that interacts with an independent third-party application.
## Writing Tests
We use class based tests. Each class will require a `setUpTestData` method for test setup. To furhter assist in the writing of tests, we have written the test cases for common items as an abstract class. You are advised to review the [test cases](./api/tests/index.md) and if it's applicable to the item you have added, than add the test case class to be inherited by your test class.
naming of test classes is in `CamelCase` in format `<Model Name><what's being tested>` for example the class name for device model history entry tests would be `DeviceHistory`.
Naming of test classes is in `CamelCase` in format `<Model Name><what's being tested>` for example the class name for device model history entry tests would be `DeviceHistory`.
Test setup is written in a method called `setUpTestData` and is to contain the setup for all tests within the test class.
@ -51,22 +55,40 @@ class DeviceHistory(TestCase, HistoryEntry, HistoryEntryParentItem):
Each module is to contain a tests directory of the model being tested with a single file for grouping of what is being tested. for items that depend upon a parent model, the test file is to be within the child-models test directory named with format `test_<model>_<parent app>_<parent model name>`
_example file system structure for the device model that relies upon access app model organization, core app model history and model notes._
_example file system structure showing the layout of the tests directory for a module_
``` text
.
├── tests
│   ├── <model name>
│   │   ├── test_<model name>_access_organization.py
│   │   ── test_<model name>_api_permission.py
│   │   ── test_<model name>_core_history.py
│   │   ├── test_<model name>_core_notes.py
│   │   ├── test_<model name>_permission.py
│   │   └── test_device.py
│   ├── functional
│   │ ├── __init__.py
│   │   ── <model name>
│   │   ── test_<model name>_a_tast_name.py
│   ├── __init__.py
│   ├── integration
│   │ ├── __init__.py
│   │   └── <model name>
│   │   └── test_<model name>_a_tast_name.py
│   ├── ui
│   │ ├── __init__.py
│   │   └── <model name>
│   │   └── test_<model name>_a_tast_name.py
│   └── unit
│   ├── __init__.py
│   └── <model name>
│      ├── test_<model name>_api.py
│      ├── test_<model name>_permission_api.py
│      ├── test_<model name>_permission.py
│      ├── test_<model name>_core_history.py
│      ├── test_<model name>_history_permission.py
│      ├── test_<model name>.py
│      └── test_<model name>_views.py
```
Tests are broken up into the type the test is (sub-directory to test), and they are `unit`, `functional`, `UI` and `integration`. These sub-directories each contain a sub-directory for each model they are testing.
Items to test include, and are not limited to:
- CRUD permissions admin site
@ -105,6 +127,10 @@ Items to test include, and are not limited to:
_applicable if notes are able to be added to an item._
- API Fields
_Field exists, Type is checked_
## Running Tests

View File

@ -8,15 +8,16 @@ about: https://gitlab.com/nofusscomputing/infrastructure/configuration-managemen
<span style="text-align: center;">
![Project Status - Active](https://img.shields.io/badge/Project%20Status-Active-green?logo=gitlab&style=plastic)
![Project Status - Active](https://img.shields.io/badge/Project%20Status-Active-green?logo=github&style=plastic)
![Gitlab build status - stable](https://img.shields.io/badge/dynamic/json?color=ff782e&label=Stable%20Build&query=0.status&url=https%3A%2F%2Fgitlab.com%2Fapi%2Fv4%2Fprojects%2F57560288%2Fpipelines%3Fref%3Dmaster&logo=gitlab&style=plastic) ![Gitlab build status - development](https://img.shields.io/badge/dynamic/json?color=ff782e&label=Dev%20Build&query=0.status&url=https%3A%2F%2Fgitlab.com%2Fapi%2Fv4%2Fprojects%2F57560288%2Fpipelines%3Fref%3Ddevelopment&logo=gitlab&style=plastic)
![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/nofusscomputing/centurion_erp/ci.yaml?branch=master&style=plastic&logo=github&label=Stable%20Build&color=%23000) ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/nofusscomputing/centurion_erp/ci.yaml?branch=development&style=plastic&logo=github&label=Dev%20Build&color=%23000)
![GitLab Issues](https://img.shields.io/gitlab/issues/open/nofusscomputing%2Fprojects%2Fcenturion_erp?style=plastic&logo=gitlab&label=Issues&color=fc6d26) [![GitLab Bugs](https://img.shields.io/gitlab/issues/open/nofusscomputing%2Fprojects%2Fcenturion_erp?labels=type%3A%3Abug&style=plastic&logo=gitlab&label=Bug%20Fixes%20Required&color=fc6d26)](https://gitlab.com/nofusscomputing/projects/centurion_erp/-/issues/?sort=created_date&state=opened&label_name%5B%5D=type%3A%3Abug)
![GitHub Issues or Pull Requests](https://img.shields.io/github/issues/nofusscomputing/centurion_erp?style=plastic&logo=github&label=Open%20Issues&color=000) ![GitHub Issues or Pull Requests by label](https://img.shields.io/github/issues/nofusscomputing/centurion_erp/type%3A%3Abug?style=plastic&logo=github&label=Bug%20Fixes%20Required&color=000)
![Gitlab Code Coverage](https://img.shields.io/gitlab/pipeline-coverage/nofusscomputing%2Fprojects%2Fcenturion_erp?branch=master&style=plastic&logo=gitlab&label=Test%20Coverage)
![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fnofusscomputing%2F.github%2Fmaster%2Frepositories%2Fnofusscomputing%2Fcenturion_erp%2Fdevelopment%2Fbadge_endpoint_coverage.json&style=plastic) ![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fnofusscomputing%2F.github%2Fmaster%2Frepositories%2Fnofusscomputing%2Fcenturion_erp%2Fdevelopment%2Fbadge_endpoint_unit_test.json)
![Docker Pulls](https://img.shields.io/docker/pulls/nofusscomputing/centurion-erp?style=plastic&logo=docker&color=0db7ed)
![Docker Pulls](https://img.shields.io/docker/pulls/nofusscomputing/centurion-erp?style=plastic&logo=docker&color=0db7ed) [![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/centurion-erp)](https://artifacthub.io/packages/container/centurion-erp/centurion-erp)
</span>

View File

@ -0,0 +1,39 @@
### :books: Summary
<!-- your summary here emojis ref: https://github.com/yodamad/gitlab-emoji -->
### :link: Links / References
<!--
using a list as any links to other references or links as required. if relevent, describe the link/reference
Include any issues or related merge requests. Note: dependent MR's also to be added to "Merge request dependencies"
-->
### :construction_worker: Tasks
- [ ] Add your tasks here as required (delete task if n/a)
<!-- dont remove tasks below strike through including the checkbox by enclosing in double tidle '~~' -->
- [ ] :orange_circle: Related issue(s) closed via [commit message](https://www.conventionalcommits.org/en/v1.0.0) footer
- [ ] :yellow_circle: Contains `breaking-change` Any Breaking change(s)? [commit message](https://www.conventionalcommits.org/en/v1.0.0)
_Breaking Change must also be notated in the commit that introduces it and in [Conventional Commit Format](https://www.conventionalcommits.org/en/v1.0.0/)._
- [ ] :memo: Release notes updated
- [ ] :large_blue_circle: Documentation Documentation written
_All features to be documented within the correct section(s). Administration, Development and/or User_
- [ ] Milestone assigned
- [ ] :red_circle: [Unit Test(s) Written](https://nofusscomputing.com/projects/centurion_erp/development/testing/)
_ensure test coverage delta is not less than zero_

View File

@ -3,8 +3,8 @@ INHERIT: website-template/mkdocs.yml
docs_dir: 'docs'
repo_name: Centurion ERP
repo_url: https://gitlab.com/nofusscomputing/projects/centurion_erp
edit_uri: '/-/ide/project/nofusscomputing/projects/centurion_erp/edit/development/-/docs/'
repo_url: https://github.com/nofusscomputing/centurion_erp
edit_uri: '/edit/development/docs/'
plugins:
mkdocstrings:

View File

@ -1,4 +1,4 @@
Django==5.0.7
Django==5.0.8
django-debug-toolbar==4.3.0
social-auth-app-django==5.4.1