PyInventory 0.19.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- PyInventory-0.19.0.dist-info/AUTHORS +14 -0
- PyInventory-0.19.0.dist-info/LICENSE +674 -0
- PyInventory-0.19.0.dist-info/METADATA +347 -0
- PyInventory-0.19.0.dist-info/RECORD +101 -0
- PyInventory-0.19.0.dist-info/WHEEL +5 -0
- PyInventory-0.19.0.dist-info/entry_points.txt +2 -0
- PyInventory-0.19.0.dist-info/top_level.txt +2 -0
- inventory/__init__.py +7 -0
- inventory/admin/__init__.py +3 -0
- inventory/admin/base.py +104 -0
- inventory/admin/item.py +169 -0
- inventory/admin/location.py +78 -0
- inventory/admin/memo.py +76 -0
- inventory/admin/tagulous_fix.py +45 -0
- inventory/apps.py +18 -0
- inventory/ckeditor_upload.py +15 -0
- inventory/context_processors.py +5 -0
- inventory/forms.py +36 -0
- inventory/locale/ca/LC_MESSAGES/django.mo +0 -0
- inventory/locale/ca/LC_MESSAGES/django.po +297 -0
- inventory/locale/de/LC_MESSAGES/django.mo +0 -0
- inventory/locale/de/LC_MESSAGES/django.po +294 -0
- inventory/locale/en/LC_MESSAGES/django.mo +0 -0
- inventory/locale/en/LC_MESSAGES/django.po +294 -0
- inventory/locale/es/LC_MESSAGES/django.mo +0 -0
- inventory/locale/es/LC_MESSAGES/django.po +297 -0
- inventory/management/__init__.py +0 -0
- inventory/management/commands/__init__.py +0 -0
- inventory/management/commands/seed_data.py +135 -0
- inventory/management/commands/tree.py +62 -0
- inventory/middlewares.py +21 -0
- inventory/migrations/0001_initial.py +596 -0
- inventory/migrations/0002_auto_20201017_2211.py +87 -0
- inventory/migrations/0003_auto_20201024_1830.py +23 -0
- inventory/migrations/0004_item_user_images.py +129 -0
- inventory/migrations/0005_serve_uploads_by_django_tools.py +77 -0
- inventory/migrations/0006_refactor_image_model.py +46 -0
- inventory/migrations/0007_add_file_attachment.py +128 -0
- inventory/migrations/0008_last_check_datetime.py +23 -0
- inventory/migrations/0009_add_memo.py +517 -0
- inventory/migrations/0010_version_protect_models.py +37 -0
- inventory/migrations/0011_parent_tree1.py +97 -0
- inventory/migrations/0012_parent_tree2.py +20 -0
- inventory/migrations/0013_alter_itemmodel_location.py +26 -0
- inventory/migrations/__init__.py +0 -0
- inventory/models/__init__.py +3 -0
- inventory/models/base.py +239 -0
- inventory/models/item.py +228 -0
- inventory/models/links.py +104 -0
- inventory/models/location.py +24 -0
- inventory/models/memo.py +109 -0
- inventory/parent_tree.py +71 -0
- inventory/permissions.py +60 -0
- inventory/request_dict.py +16 -0
- inventory/signals.py +15 -0
- inventory/string_utils.py +15 -0
- inventory/templates/admin/item/related_items.html +18 -0
- inventory/templates/admin/location/items.html +18 -0
- inventory/tests/__init__.py +0 -0
- inventory/tests/fixtures/__init__.py +0 -0
- inventory/tests/fixtures/users.py +11 -0
- inventory/tests/test_admin_location.py +34 -0
- inventory/tests/test_admin_location_empty_change_list_1.snapshot.html +84 -0
- inventory/tests/test_item_images.py +76 -0
- inventory/tests/test_link_model.py +72 -0
- inventory/tests/test_management_command_seed_data.py +49 -0
- inventory/tests/test_management_command_tree.py +27 -0
- inventory/tests/test_parent_tree.py +40 -0
- inventory/tests/test_parent_tree_model.py +139 -0
- inventory_project/__init__.py +12 -0
- inventory_project/__main__.py +17 -0
- inventory_project/manage.py +41 -0
- inventory_project/middlewares.py +23 -0
- inventory_project/publish.py +21 -0
- inventory_project/settings/__init__.py +0 -0
- inventory_project/settings/local.py +74 -0
- inventory_project/settings/prod.py +393 -0
- inventory_project/settings/tests.py +45 -0
- inventory_project/templates/admin/base_site.html +22 -0
- inventory_project/templates/admin/login.html +32 -0
- inventory_project/tests/__init__.py +0 -0
- inventory_project/tests/fixtures.py +40 -0
- inventory_project/tests/mocks.py +15 -0
- inventory_project/tests/playwright_utils.py +22 -0
- inventory_project/tests/test_admin.py +15 -0
- inventory_project/tests/test_admin_item.py +240 -0
- inventory_project/tests/test_admin_item_auto_group_items_1.snapshot.html +349 -0
- inventory_project/tests/test_admin_item_auto_group_items_2.snapshot.html +232 -0
- inventory_project/tests/test_admin_item_login_1.snapshot.html +40 -0
- inventory_project/tests/test_admin_item_normal_user_create_minimal_item_1.snapshot.html +637 -0
- inventory_project/tests/test_admin_item_normal_user_create_minimal_item_2.snapshot.html +930 -0
- inventory_project/tests/test_admin_memo.py +153 -0
- inventory_project/tests/test_admin_memo_normal_user_create_minimal_item_1.snapshot.html +365 -0
- inventory_project/tests/test_command_shell_help_django4.2.3.snapshot.txt +60 -0
- inventory_project/tests/test_inventory_commands.py +26 -0
- inventory_project/tests/test_migrations.py +22 -0
- inventory_project/tests/test_models_item.py +24 -0
- inventory_project/tests/test_playwright_admin.py +157 -0
- inventory_project/tests/test_project_setup.py +102 -0
- inventory_project/urls.py +21 -0
- inventory_project/wsgi.py +9 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
from bx_django_utils.test_utils.playwright import PlaywrightTestCase
|
|
2
|
+
from django.contrib.auth import authenticate
|
|
3
|
+
from django.contrib.auth.models import User
|
|
4
|
+
from django.http import HttpRequest
|
|
5
|
+
from django.test import override_settings
|
|
6
|
+
from override_storage import locmem_stats_override_storage
|
|
7
|
+
from playwright.sync_api import BrowserContext, expect
|
|
8
|
+
|
|
9
|
+
from inventory import __version__
|
|
10
|
+
from inventory.models import ItemImageModel, ItemLinkModel, ItemModel
|
|
11
|
+
from inventory_project.tests.fixtures import TempImageFile, get_normal_user
|
|
12
|
+
from inventory_project.tests.playwright_utils import login
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@override_settings(SECURE_SSL_REDIRECT=False)
|
|
16
|
+
class PlaywrightInventoryTestCase(PlaywrightTestCase):
|
|
17
|
+
def test_root_page(self):
|
|
18
|
+
context: BrowserContext = self.browser.new_context(
|
|
19
|
+
ignore_https_errors=True,
|
|
20
|
+
locale='en_US',
|
|
21
|
+
timezone_id='Europe/Berlin',
|
|
22
|
+
)
|
|
23
|
+
with context.new_page() as page:
|
|
24
|
+
page.goto(self.live_server_url)
|
|
25
|
+
expect(page).to_have_url(f'{self.live_server_url}/admin/login/?next=/admin/')
|
|
26
|
+
expect(page).to_have_title(f'Log in | PyInventory v{__version__}')
|
|
27
|
+
|
|
28
|
+
def test_login(self):
|
|
29
|
+
username = 'a-user'
|
|
30
|
+
password = 'ThisIsNotAPassword!'
|
|
31
|
+
superuser = User.objects.create_superuser(username=username, password=password)
|
|
32
|
+
superuser.full_clean()
|
|
33
|
+
|
|
34
|
+
user = authenticate(request=HttpRequest(), username=username, password=password)
|
|
35
|
+
assert isinstance(user, User)
|
|
36
|
+
|
|
37
|
+
context: BrowserContext = self.browser.new_context(
|
|
38
|
+
ignore_https_errors=True,
|
|
39
|
+
locale='en_US',
|
|
40
|
+
timezone_id='Europe/Berlin',
|
|
41
|
+
)
|
|
42
|
+
with context.new_page() as page:
|
|
43
|
+
page.goto(self.live_server_url)
|
|
44
|
+
expect(page).to_have_url(f'{self.live_server_url}/admin/login/?next=/admin/')
|
|
45
|
+
expect(page).to_have_title(f'Log in | PyInventory v{__version__}')
|
|
46
|
+
|
|
47
|
+
page.type('#id_username', username)
|
|
48
|
+
page.type('#id_password', password)
|
|
49
|
+
page.locator('text=Log in').click()
|
|
50
|
+
|
|
51
|
+
expect(page).to_have_url(f'{self.live_server_url}/admin/')
|
|
52
|
+
expect(page).to_have_title(f'Site administration | PyInventory v{__version__}')
|
|
53
|
+
|
|
54
|
+
def test_admin(self):
|
|
55
|
+
superuser = User.objects.create_superuser(username='foo', password='ThisIsNotAPassword!')
|
|
56
|
+
superuser.full_clean()
|
|
57
|
+
|
|
58
|
+
context: BrowserContext = self.browser.new_context(
|
|
59
|
+
ignore_https_errors=True,
|
|
60
|
+
locale='en_US',
|
|
61
|
+
timezone_id='Europe/Berlin',
|
|
62
|
+
)
|
|
63
|
+
with context.new_page() as page:
|
|
64
|
+
login(page, self.client, url=self.live_server_url, user=superuser)
|
|
65
|
+
|
|
66
|
+
page.goto(f'{self.live_server_url}/admin/')
|
|
67
|
+
expect(page).to_have_url(f'{self.live_server_url}/admin/')
|
|
68
|
+
expect(page).to_have_title(f'Site administration | PyInventory v{__version__}')
|
|
69
|
+
|
|
70
|
+
def test_normal_user_create_item(self):
|
|
71
|
+
normal_user = get_normal_user()
|
|
72
|
+
|
|
73
|
+
context: BrowserContext = self.browser.new_context(
|
|
74
|
+
ignore_https_errors=True,
|
|
75
|
+
locale='en_US',
|
|
76
|
+
timezone_id='Europe/Berlin',
|
|
77
|
+
)
|
|
78
|
+
with context.new_page() as page, TempImageFile(
|
|
79
|
+
format='png', size=(1, 1)
|
|
80
|
+
) as png_image, locmem_stats_override_storage() as storage_stats:
|
|
81
|
+
login(page, self.client, url=self.live_server_url, user=normal_user)
|
|
82
|
+
|
|
83
|
+
page.goto(f'{self.live_server_url}/admin/inventory/itemmodel/add/')
|
|
84
|
+
expect(page).to_have_title(f'Add Item | PyInventory v{__version__}')
|
|
85
|
+
|
|
86
|
+
page.locator('label:has-text("Kind:")')
|
|
87
|
+
kind_field = page.locator('//input[@id="id_kind"]/..//input[@role="searchbox"]')
|
|
88
|
+
kind_field.click()
|
|
89
|
+
kind_field.fill('Mainboard')
|
|
90
|
+
kind_field.press('Enter')
|
|
91
|
+
|
|
92
|
+
page.locator('label:has-text("Producer:")')
|
|
93
|
+
producer_field = page.locator('//input[@id="id_producer"]/..//input[@role="searchbox"]')
|
|
94
|
+
producer_field.click()
|
|
95
|
+
producer_field.fill('Triple D Int.Ltd.')
|
|
96
|
+
producer_field.press('Enter')
|
|
97
|
+
|
|
98
|
+
page.locator('label:has-text("Name:")')
|
|
99
|
+
page.fill('//input[@id="id_name"]', 'TD-20 (8088)')
|
|
100
|
+
|
|
101
|
+
# Add a Link:
|
|
102
|
+
page.get_by_role('link', name='Add another Link').click()
|
|
103
|
+
page.locator('#id_itemlinkmodel_set-0-url').click()
|
|
104
|
+
page.locator('#id_itemlinkmodel_set-0-url').fill('http://test.tld/foo/bar')
|
|
105
|
+
page.locator('#id_itemlinkmodel_set-0-name').click()
|
|
106
|
+
page.locator('#id_itemlinkmodel_set-0-name').fill('The First Link')
|
|
107
|
+
page.locator('#id_itemlinkmodel_set-0-tags').click()
|
|
108
|
+
page.locator('#id_itemlinkmodel_set-0-tags').fill('a-link-tag')
|
|
109
|
+
page.locator('#id_itemlinkmodel_set-0-tags').press('Tab')
|
|
110
|
+
|
|
111
|
+
# Add Image
|
|
112
|
+
page.get_by_role('link', name='Add another Image').click()
|
|
113
|
+
page.locator('#id_itemimagemodel_set-0-image').click()
|
|
114
|
+
page.locator('#id_itemimagemodel_set-0-image').set_input_files(png_image.name)
|
|
115
|
+
page.locator('#id_itemimagemodel_set-0-name').click()
|
|
116
|
+
page.locator('#id_itemimagemodel_set-0-name').fill('The Image Name')
|
|
117
|
+
page.locator('#id_itemimagemodel_set-0-tags').click()
|
|
118
|
+
page.locator('#id_itemimagemodel_set-0-tags').fill('a-image-tag')
|
|
119
|
+
page.locator('#id_itemimagemodel_set-0-tags').press('Tab')
|
|
120
|
+
|
|
121
|
+
assert ItemModel.objects.count() == 0
|
|
122
|
+
|
|
123
|
+
# Save the item:
|
|
124
|
+
page.locator('input:has-text("Save and continue editing")').click()
|
|
125
|
+
page.locator('text=The Tunes Item “A Test Tunes Item” was added successfully. You may edit it again')
|
|
126
|
+
page.locator('text="Triple D Int.Ltd." - TD-20 (8088)')
|
|
127
|
+
|
|
128
|
+
assert ItemModel.objects.count() == 1
|
|
129
|
+
item = ItemModel.objects.first()
|
|
130
|
+
assert item.verbose_name() == 'Mainboard - "Triple D Int.Ltd." - TD-20 (8088)'
|
|
131
|
+
|
|
132
|
+
# Save & continue?
|
|
133
|
+
expect(page).to_have_url(f'{self.live_server_url}/admin/inventory/itemmodel/{item.id}/change/')
|
|
134
|
+
|
|
135
|
+
# Check added image:
|
|
136
|
+
page.locator('text=The Image Name')
|
|
137
|
+
img = page.locator('//a[@class="image_file_input_preview"]/img')
|
|
138
|
+
img.scroll_into_view_if_needed()
|
|
139
|
+
img.is_visible()
|
|
140
|
+
assert img.evaluate('image => image.complete') is True
|
|
141
|
+
|
|
142
|
+
assert item.itemimagemodel_set.count() == 1
|
|
143
|
+
image: ItemImageModel = item.itemimagemodel_set.first()
|
|
144
|
+
assert str(image) == 'The Image Name'
|
|
145
|
+
assert image.user == normal_user
|
|
146
|
+
assert image.tags.get_tag_list() == ['a-image-tag']
|
|
147
|
+
|
|
148
|
+
# Check the added link:
|
|
149
|
+
page.locator('text=The First Link')
|
|
150
|
+
page.locator('text=Currently: http://test.tld/foo/bar')
|
|
151
|
+
links = list(item.itemlinkmodel_set.values_list('name', 'url'))
|
|
152
|
+
assert links == [('The First Link', 'http://test.tld/foo/bar')]
|
|
153
|
+
link: ItemLinkModel = item.itemlinkmodel_set.first()
|
|
154
|
+
assert link.tags.get_tag_list() == ['a-link-tag']
|
|
155
|
+
|
|
156
|
+
# The "png_image" file should be stored:
|
|
157
|
+
self.assertEqual(storage_stats.save_cnt, 1)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from unittest import TestCase
|
|
4
|
+
|
|
5
|
+
from bx_py_utils.path import assert_is_dir, assert_is_file
|
|
6
|
+
from django.conf import settings
|
|
7
|
+
from django.core import checks
|
|
8
|
+
from django.core.cache import cache
|
|
9
|
+
from django.core.management import call_command
|
|
10
|
+
from manage_django_project.management.commands import code_style
|
|
11
|
+
from manageprojects.test_utils.project_setup import check_editor_config, get_py_max_line_length
|
|
12
|
+
from packaging.version import Version
|
|
13
|
+
|
|
14
|
+
from inventory import __version__
|
|
15
|
+
from manage import BASE_PATH
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ProjectSetupTestCase(TestCase):
|
|
19
|
+
def test_project_path(self):
|
|
20
|
+
project_path = settings.BASE_PATH
|
|
21
|
+
assert_is_dir(project_path)
|
|
22
|
+
assert_is_dir(project_path / 'inventory')
|
|
23
|
+
assert_is_dir(project_path / 'inventory_project')
|
|
24
|
+
|
|
25
|
+
self.assertEqual(project_path, BASE_PATH)
|
|
26
|
+
|
|
27
|
+
def test_template_dirs(self):
|
|
28
|
+
assert len(settings.TEMPLATES) == 1
|
|
29
|
+
dirs = settings.TEMPLATES[0].get('DIRS')
|
|
30
|
+
assert len(dirs) == 1
|
|
31
|
+
template_path = Path(dirs[0]).resolve()
|
|
32
|
+
assert template_path.is_dir()
|
|
33
|
+
|
|
34
|
+
def test_manage_check(self):
|
|
35
|
+
all_issues = checks.run_checks(
|
|
36
|
+
app_configs=None,
|
|
37
|
+
tags=None,
|
|
38
|
+
include_deployment_checks=True,
|
|
39
|
+
databases=None,
|
|
40
|
+
)
|
|
41
|
+
all_issue_ids = {issue.id for issue in all_issues}
|
|
42
|
+
excpeted_issues = {
|
|
43
|
+
'async.E001', # os.environ['DJANGO_ALLOW_ASYNC_UNSAFE'] exists
|
|
44
|
+
}
|
|
45
|
+
if all_issue_ids != excpeted_issues:
|
|
46
|
+
print('=' * 100)
|
|
47
|
+
for issue in all_issues:
|
|
48
|
+
print(issue)
|
|
49
|
+
print('=' * 100)
|
|
50
|
+
raise AssertionError(f'There are check issues (see blow): {all_issue_ids ^ excpeted_issues}')
|
|
51
|
+
|
|
52
|
+
def test_cache(self):
|
|
53
|
+
# django cache should work in tests, because some tests "depends" on it
|
|
54
|
+
cache_key = 'a-cache-key'
|
|
55
|
+
self.assertIs(cache.get(cache_key), None)
|
|
56
|
+
cache.set(cache_key, 'the cache content', timeout=1)
|
|
57
|
+
self.assertEqual(cache.get(cache_key), 'the cache content', f'Check: {settings.CACHES=}')
|
|
58
|
+
cache.delete(cache_key)
|
|
59
|
+
self.assertIs(cache.get(cache_key), None)
|
|
60
|
+
|
|
61
|
+
def test_settings(self):
|
|
62
|
+
self.assertEqual(settings.SETTINGS_MODULE, 'inventory_project.settings.tests')
|
|
63
|
+
middlewares = [entry.rsplit('.', 1)[-1] for entry in settings.MIDDLEWARE]
|
|
64
|
+
assert 'AlwaysLoggedInAsSuperUserMiddleware' not in middlewares
|
|
65
|
+
assert 'DebugToolbarMiddleware' not in middlewares
|
|
66
|
+
|
|
67
|
+
def test_version(self):
|
|
68
|
+
self.assertIsNotNone(__version__)
|
|
69
|
+
|
|
70
|
+
version = Version(__version__) # Will raise InvalidVersion() if wrong formatted
|
|
71
|
+
self.assertEqual(str(version), __version__)
|
|
72
|
+
|
|
73
|
+
manage_bin = BASE_PATH / 'manage.py'
|
|
74
|
+
assert_is_file(manage_bin)
|
|
75
|
+
|
|
76
|
+
output = subprocess.check_output([manage_bin, 'version'], text=True)
|
|
77
|
+
self.assertIn(__version__, output)
|
|
78
|
+
|
|
79
|
+
def test_manage(self):
|
|
80
|
+
manage_bin = BASE_PATH / 'manage.py'
|
|
81
|
+
assert_is_file(manage_bin)
|
|
82
|
+
|
|
83
|
+
output = subprocess.check_output([manage_bin, 'project_info'], text=True)
|
|
84
|
+
self.assertIn('inventory_project', output)
|
|
85
|
+
self.assertIn('inventory_project.settings.local', output)
|
|
86
|
+
self.assertIn('inventory_project.settings.tests', output)
|
|
87
|
+
self.assertIn(__version__, output)
|
|
88
|
+
|
|
89
|
+
output = subprocess.check_output([manage_bin, 'check'], text=True)
|
|
90
|
+
self.assertIn('System check identified no issues (0 silenced).', output)
|
|
91
|
+
|
|
92
|
+
output = subprocess.check_output([manage_bin, 'makemigrations'], text=True)
|
|
93
|
+
self.assertIn("No changes detected", output)
|
|
94
|
+
|
|
95
|
+
def test_code_style(self):
|
|
96
|
+
call_command(code_style.Command())
|
|
97
|
+
|
|
98
|
+
def test_check_editor_config(self):
|
|
99
|
+
check_editor_config(package_root=BASE_PATH)
|
|
100
|
+
|
|
101
|
+
max_line_length = get_py_max_line_length(package_root=BASE_PATH)
|
|
102
|
+
self.assertEqual(max_line_length, 119)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
from django.conf.urls import include
|
|
3
|
+
from django.contrib import admin
|
|
4
|
+
from django.urls import path, re_path
|
|
5
|
+
from django.views.generic import RedirectView
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
admin.autodiscover()
|
|
9
|
+
|
|
10
|
+
urlpatterns = [ # Don't use i18n_patterns() here
|
|
11
|
+
path('admin/', admin.site.urls),
|
|
12
|
+
re_path(r'^$', RedirectView.as_view(pattern_name='admin:index')),
|
|
13
|
+
path('ckeditor/', include('ckeditor_uploader.urls')), # TODO: check permissions?
|
|
14
|
+
path(settings.MEDIA_URL.lstrip('/'), include('django_tools.serve_media_app.urls')),
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
if settings.DEBUG:
|
|
19
|
+
import debug_toolbar
|
|
20
|
+
|
|
21
|
+
urlpatterns = [re_path(r'^__debug__/', include(debug_toolbar.urls))] + urlpatterns
|