Merge branch 'testing' into feature/22-device-lot-visibility
# Conflicts: # ereuse_devicehub/resources/device/views.py
This commit is contained in:
commit
e372811d6e
71
.github/workflows/flask.yml
vendored
Normal file
71
.github/workflows/flask.yml
vendored
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
name: Flask CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master, testing]
|
||||||
|
pull_request:
|
||||||
|
branches: [master, testing]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
# Service containers to run with `container-job`
|
||||||
|
services:
|
||||||
|
# Label used to access the service container
|
||||||
|
postgres:
|
||||||
|
# Docker Hub image
|
||||||
|
image: postgres:11
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
# Set health checks to wait until postgres has started
|
||||||
|
options: >-
|
||||||
|
--health-cmd pg_isready
|
||||||
|
--health-interval 10s
|
||||||
|
--health-timeout 5s
|
||||||
|
--health-retries 5
|
||||||
|
env:
|
||||||
|
POSTGRES_DB: dh_test
|
||||||
|
POSTGRES_USER: dhub
|
||||||
|
POSTGRES_PASSWORD: ereuse
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
max-parallel: 4
|
||||||
|
matrix:
|
||||||
|
python-version: [3.7]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v1
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -qy
|
||||||
|
sudo apt-get -y install postgresql-client
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install virtualenv
|
||||||
|
virtualenv env
|
||||||
|
source env/bin/activate
|
||||||
|
pip install flake8 pytest
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
- name: Prepare database
|
||||||
|
env:
|
||||||
|
POSTGRES_DB: dh_test
|
||||||
|
POSTGRES_USER: dhub
|
||||||
|
POSTGRES_PASSWORD: ereuse
|
||||||
|
run: |
|
||||||
|
export PGPASSWORD=$POSTGRES_PASSWORD
|
||||||
|
psql -h "localhost" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "CREATE EXTENSION pgcrypto SCHEMA public;"
|
||||||
|
psql -h "localhost" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "CREATE EXTENSION ltree SCHEMA public;"
|
||||||
|
psql -h "localhost" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "CREATE EXTENSION citext SCHEMA public;"
|
||||||
|
psql -h "localhost" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "CREATE EXTENSION pg_trgm SCHEMA public;"
|
||||||
|
|
||||||
|
- name: Run Tests
|
||||||
|
run: |
|
||||||
|
source env/bin/activate
|
||||||
|
pytest -m mvp --maxfail=5 tests/
|
||||||
|
|
96
README.rst
96
README.rst
|
@ -21,13 +21,10 @@ The requirements are:
|
||||||
`dependencies <http://weasyprint.readthedocs.io/en/stable/install.html>`__.
|
`dependencies <http://weasyprint.readthedocs.io/en/stable/install.html>`__.
|
||||||
|
|
||||||
Install Devicehub with *pip*:
|
Install Devicehub with *pip*:
|
||||||
``pip3 install ereuse-devicehub -U --pre``.
|
``pip3 install -U -r requirements.txt -e .``.
|
||||||
|
|
||||||
Running
|
Running
|
||||||
*******
|
*******
|
||||||
Download, or copy the contents, of `this file <examples/app.py>`__, and
|
|
||||||
call the new file ``app.py``.
|
|
||||||
|
|
||||||
Create a PostgreSQL database called *devicehub* by running
|
Create a PostgreSQL database called *devicehub* by running
|
||||||
`create-db <examples/create-db.sh>`__:
|
`create-db <examples/create-db.sh>`__:
|
||||||
|
|
||||||
|
@ -40,28 +37,18 @@ Create a PostgreSQL database called *devicehub* by running
|
||||||
- In MacOS: ``bash examples/create-db.sh devicehub dhub``, and password
|
- In MacOS: ``bash examples/create-db.sh devicehub dhub``, and password
|
||||||
``ereuse``.
|
``ereuse``.
|
||||||
|
|
||||||
Create the tables in the database by executing in the same directory
|
Using the `dh` tool for set up with one or multiple inventories.
|
||||||
where ``app.py`` is:
|
Create the tables in the database by executing:
|
||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
$ flask init-db
|
$ export dhi=dbtest; dh inv add --common --name dbtest
|
||||||
|
|
||||||
Finally, run the app:
|
Finally, run the app:
|
||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
$ flask run
|
$ export dhi=dbtest;dh run --debugger
|
||||||
|
|
||||||
The error ``flask: command not found`` can happen when you are not in a
|
|
||||||
*virtual environment*. Try executing then ``python3 -m flask``.
|
|
||||||
|
|
||||||
Execute ``flask`` only to know all the administration options Devicehub
|
|
||||||
offers.
|
|
||||||
|
|
||||||
See the `Flask
|
|
||||||
quickstart <http://flask.pocoo.org/docs/1.0/quickstart/>`__ for more
|
|
||||||
info.
|
|
||||||
|
|
||||||
The error ‘bdist_wheel’ can happen when you work with a *virtual environment*.
|
The error ‘bdist_wheel’ can happen when you work with a *virtual environment*.
|
||||||
To fix it, install in the *virtual environment* wheel
|
To fix it, install in the *virtual environment* wheel
|
||||||
|
@ -70,9 +57,14 @@ package. ``pip3 install wheel``
|
||||||
Multiple instances
|
Multiple instances
|
||||||
------------------
|
------------------
|
||||||
Devicehub can run as a single inventory or with multiple inventories,
|
Devicehub can run as a single inventory or with multiple inventories,
|
||||||
each inventory being an instance of the ``devicehub``. To execute
|
each inventory being an instance of the ``devicehub``. To add a new inventory
|
||||||
one instance, use the ``flask`` command, to execute multiple instances
|
execute:
|
||||||
use the ``dh`` command. The ``dh`` command is like ``flask``, but
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
$ export dhi=dbtest; dh inv add --name dbtest
|
||||||
|
|
||||||
|
Note: The ``dh`` command is like ``flask``, but
|
||||||
it allows you to create and delete instances, and interface to them
|
it allows you to create and delete instances, and interface to them
|
||||||
directly.
|
directly.
|
||||||
|
|
||||||
|
@ -86,6 +78,68 @@ Testing
|
||||||
password ``ereuse``.
|
password ``ereuse``.
|
||||||
3. Execute at the root folder of the project ``python3 setup.py test``.
|
3. Execute at the root folder of the project ``python3 setup.py test``.
|
||||||
|
|
||||||
|
|
||||||
|
Migrations
|
||||||
|
**********
|
||||||
|
At this stage, migration files are created manually.
|
||||||
|
Set up the database:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
$ sudo su - postgres
|
||||||
|
$ bash $PATH_TO_DEVIHUBTEAL/examples/create-db.sh devicehub dhub
|
||||||
|
|
||||||
|
Initialize the database:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
$ export dhi=dbtest; dh inv add --common --name dbtest
|
||||||
|
|
||||||
|
This command will create the schemas, tables in the specified database.
|
||||||
|
Then we need to stamp the initial migration.
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
$ alembic stamp head
|
||||||
|
|
||||||
|
|
||||||
|
This command will set the revision **fbb7e2a0cde0_initial** as our initial migration.
|
||||||
|
For more info in migration stamping please see https://alembic.sqlalchemy.org/en/latest/cookbook.html
|
||||||
|
|
||||||
|
|
||||||
|
Whenever a change needed eg to create a new schema, alter an existing table, column or perform any
|
||||||
|
operation on tables, create a new revision file:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
$ alembic revision -m "A table change"
|
||||||
|
|
||||||
|
This command will create a new revision file with name `<revision_id>_a_table_change`.
|
||||||
|
Edit the generated file with the necessary operations to perform the migration:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
$ alembic edit <revision_id>
|
||||||
|
|
||||||
|
Apply migrations using:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
$ alembic -x inventory=dbtest upgrade head
|
||||||
|
|
||||||
|
Then to go back to previous db version:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
$ alembic -x inventory=dbtest downgrade <revision_id>
|
||||||
|
|
||||||
|
To see a full list of migrations use
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
$ alembic history
|
||||||
|
|
||||||
|
|
||||||
Generating the docs
|
Generating the docs
|
||||||
*******************
|
*******************
|
||||||
|
|
||||||
|
|
74
alembic.ini
Normal file
74
alembic.ini
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# A generic, single database configuration.
|
||||||
|
|
||||||
|
[alembic]
|
||||||
|
# path to migration scripts
|
||||||
|
script_location = ereuse_devicehub/migrations
|
||||||
|
|
||||||
|
# template used to generate migration files
|
||||||
|
# file_template = %%(rev)s_%%(slug)s
|
||||||
|
|
||||||
|
# timezone to use when rendering the date
|
||||||
|
# within the migration file as well as the filename.
|
||||||
|
# string value is passed to dateutil.tz.gettz()
|
||||||
|
# leave blank for localtime
|
||||||
|
# timezone =
|
||||||
|
|
||||||
|
# max length of characters to apply to the
|
||||||
|
# "slug" field
|
||||||
|
#truncate_slug_length = 40
|
||||||
|
|
||||||
|
# set to 'true' to run the environment during
|
||||||
|
# the 'revision' command, regardless of autogenerate
|
||||||
|
# revision_environment = false
|
||||||
|
|
||||||
|
# set to 'true' to allow .pyc and .pyo files without
|
||||||
|
# a source .py file to be detected as revisions in the
|
||||||
|
# versions/ directory
|
||||||
|
# sourceless = false
|
||||||
|
|
||||||
|
# version location specification; this defaults
|
||||||
|
# to alembic/versions. When using multiple version
|
||||||
|
# directories, initial revisions must be specified with --version-path
|
||||||
|
# version_locations = %(here)s/bar %(here)s/bat alembic/versions
|
||||||
|
|
||||||
|
# the output encoding used when revision files
|
||||||
|
# are written from script.py.mako
|
||||||
|
# output_encoding = utf-8
|
||||||
|
|
||||||
|
sqlalchemy.url = driver://user:pass@localhost/dbname
|
||||||
|
|
||||||
|
|
||||||
|
# Logging configuration
|
||||||
|
[loggers]
|
||||||
|
keys = root,sqlalchemy,alembic
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys = console
|
||||||
|
|
||||||
|
[formatters]
|
||||||
|
keys = generic
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level = WARN
|
||||||
|
handlers = console
|
||||||
|
qualname =
|
||||||
|
|
||||||
|
[logger_sqlalchemy]
|
||||||
|
level = WARN
|
||||||
|
handlers =
|
||||||
|
qualname = sqlalchemy.engine
|
||||||
|
|
||||||
|
[logger_alembic]
|
||||||
|
level = INFO
|
||||||
|
handlers =
|
||||||
|
qualname = alembic
|
||||||
|
|
||||||
|
[handler_console]
|
||||||
|
class = StreamHandler
|
||||||
|
args = (sys.stderr,)
|
||||||
|
level = NOTSET
|
||||||
|
formatter = generic
|
||||||
|
|
||||||
|
[formatter_generic]
|
||||||
|
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||||
|
datefmt = %H:%M:%S
|
74
ereuse_devicehub/alembic.ini
Normal file
74
ereuse_devicehub/alembic.ini
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# A generic, single database configuration.
|
||||||
|
|
||||||
|
[alembic]
|
||||||
|
# path to migration scripts
|
||||||
|
script_location = migrations
|
||||||
|
|
||||||
|
# template used to generate migration files
|
||||||
|
# file_template = %%(rev)s_%%(slug)s
|
||||||
|
|
||||||
|
# timezone to use when rendering the date
|
||||||
|
# within the migration file as well as the filename.
|
||||||
|
# string value is passed to dateutil.tz.gettz()
|
||||||
|
# leave blank for localtime
|
||||||
|
# timezone =
|
||||||
|
|
||||||
|
# max length of characters to apply to the
|
||||||
|
# "slug" field
|
||||||
|
#truncate_slug_length = 40
|
||||||
|
|
||||||
|
# set to 'true' to run the environment during
|
||||||
|
# the 'revision' command, regardless of autogenerate
|
||||||
|
# revision_environment = false
|
||||||
|
|
||||||
|
# set to 'true' to allow .pyc and .pyo files without
|
||||||
|
# a source .py file to be detected as revisions in the
|
||||||
|
# versions/ directory
|
||||||
|
# sourceless = false
|
||||||
|
|
||||||
|
# version location specification; this defaults
|
||||||
|
# to alembic/versions. When using multiple version
|
||||||
|
# directories, initial revisions must be specified with --version-path
|
||||||
|
# version_locations = %(here)s/bar %(here)s/bat alembic/versions
|
||||||
|
|
||||||
|
# the output encoding used when revision files
|
||||||
|
# are written from script.py.mako
|
||||||
|
# output_encoding = utf-8
|
||||||
|
|
||||||
|
sqlalchemy.url = driver://user:pass@localhost/dbname
|
||||||
|
|
||||||
|
|
||||||
|
# Logging configuration
|
||||||
|
[loggers]
|
||||||
|
keys = root,sqlalchemy,alembic
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys = console
|
||||||
|
|
||||||
|
[formatters]
|
||||||
|
keys = generic
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level = WARN
|
||||||
|
handlers = console
|
||||||
|
qualname =
|
||||||
|
|
||||||
|
[logger_sqlalchemy]
|
||||||
|
level = WARN
|
||||||
|
handlers =
|
||||||
|
qualname = sqlalchemy.engine
|
||||||
|
|
||||||
|
[logger_alembic]
|
||||||
|
level = INFO
|
||||||
|
handlers =
|
||||||
|
qualname = alembic
|
||||||
|
|
||||||
|
[handler_console]
|
||||||
|
class = StreamHandler
|
||||||
|
args = (sys.stderr,)
|
||||||
|
level = NOTSET
|
||||||
|
formatter = generic
|
||||||
|
|
||||||
|
[formatter_generic]
|
||||||
|
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||||
|
datefmt = %H:%M:%S
|
|
@ -10,6 +10,7 @@ from ereuse_utils.session import DevicehubClient
|
||||||
from flask.globals import _app_ctx_stack, g
|
from flask.globals import _app_ctx_stack, g
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
from teal.teal import Teal
|
from teal.teal import Teal
|
||||||
|
from teal.db import SchemaSQLAlchemy
|
||||||
|
|
||||||
from ereuse_devicehub.auth import Auth
|
from ereuse_devicehub.auth import Auth
|
||||||
from ereuse_devicehub.client import Client
|
from ereuse_devicehub.client import Client
|
||||||
|
@ -115,6 +116,16 @@ class Devicehub(Teal):
|
||||||
self.db.session.commit()
|
self.db.session.commit()
|
||||||
print('done.')
|
print('done.')
|
||||||
|
|
||||||
|
|
||||||
|
def _init_db(self, exclude_schema=None) -> bool:
|
||||||
|
if exclude_schema:
|
||||||
|
assert isinstance(self.db, SchemaSQLAlchemy)
|
||||||
|
self.db.create_all(exclude_schema=exclude_schema)
|
||||||
|
else:
|
||||||
|
self.db.create_all()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
@click.confirmation_option(prompt='Are you sure you want to delete the inventory {}?'
|
@click.confirmation_option(prompt='Are you sure you want to delete the inventory {}?'
|
||||||
.format(os.environ.get('dhi')))
|
.format(os.environ.get('dhi')))
|
||||||
def delete_inventory(self):
|
def delete_inventory(self):
|
||||||
|
|
|
@ -77,10 +77,12 @@ class Dummy:
|
||||||
runner.invoke('tag', 'add', id,
|
runner.invoke('tag', 'add', id,
|
||||||
'-p', 'https://t.devicetag.io',
|
'-p', 'https://t.devicetag.io',
|
||||||
'-s', sec,
|
'-s', sec,
|
||||||
|
'-u', user1.user["id"],
|
||||||
'-o', org_id)
|
'-o', org_id)
|
||||||
# create tag for pc-laudem
|
# create tag for pc-laudem
|
||||||
runner.invoke('tag', 'add', 'tagA',
|
runner.invoke('tag', 'add', 'tagA',
|
||||||
'-p', 'https://t.devicetag.io',
|
'-p', 'https://t.devicetag.io',
|
||||||
|
'-u', user1.user["id"],
|
||||||
'-s', 'tagA-secondary')
|
'-s', 'tagA-secondary')
|
||||||
files = tuple(Path(__file__).parent.joinpath('files').iterdir())
|
files = tuple(Path(__file__).parent.joinpath('files').iterdir())
|
||||||
print('done.')
|
print('done.')
|
||||||
|
@ -144,7 +146,7 @@ class Dummy:
|
||||||
res=Lot,
|
res=Lot,
|
||||||
item='{}/devices'.format(lot_user3['id']),
|
item='{}/devices'.format(lot_user3['id']),
|
||||||
query=[('id', pc) for pc in itertools.islice(pcs, 11, 14)])
|
query=[('id', pc) for pc in itertools.islice(pcs, 11, 14)])
|
||||||
|
|
||||||
lot4, _ = user4.post({},
|
lot4, _ = user4.post({},
|
||||||
res=Lot,
|
res=Lot,
|
||||||
item='{}/devices'.format(lot_user4['id']),
|
item='{}/devices'.format(lot_user4['id']),
|
||||||
|
|
|
@ -20,9 +20,6 @@ device:
|
||||||
- type: Tag
|
- type: Tag
|
||||||
id: tag1
|
id: tag1
|
||||||
actions:
|
actions:
|
||||||
- type: VisualTest
|
|
||||||
appearanceRange: A
|
|
||||||
functionalityRange: B
|
|
||||||
- type: BenchmarkRamSysbench
|
- type: BenchmarkRamSysbench
|
||||||
rate: 2444
|
rate: 2444
|
||||||
elapsed: 1
|
elapsed: 1
|
||||||
|
|
1
ereuse_devicehub/migrations/README
Normal file
1
ereuse_devicehub/migrations/README
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Generic single-database configuration.
|
88
ereuse_devicehub/migrations/env.py
Normal file
88
ereuse_devicehub/migrations/env.py
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
import os
|
||||||
|
from logging.config import fileConfig
|
||||||
|
|
||||||
|
from sqlalchemy import engine_from_config
|
||||||
|
from sqlalchemy import pool
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from alembic import context
|
||||||
|
|
||||||
|
from ereuse_devicehub.config import DevicehubConfig
|
||||||
|
|
||||||
|
# this is the Alembic Config object, which provides
|
||||||
|
# access to the values within the .ini file in use.
|
||||||
|
config = context.config
|
||||||
|
|
||||||
|
# Interpret the config file for Python logging.
|
||||||
|
# This line sets up loggers basically.
|
||||||
|
fileConfig(config.config_file_name)
|
||||||
|
|
||||||
|
# add your model's MetaData object here
|
||||||
|
# for 'autogenerate' support
|
||||||
|
# from myapp import mymodel
|
||||||
|
# target_metadata = mymodel.Base.metadata
|
||||||
|
# target_metadata = None
|
||||||
|
from ereuse_devicehub.db import db
|
||||||
|
from ereuse_devicehub.resources.models import Thing
|
||||||
|
target_metadata = Thing.metadata
|
||||||
|
|
||||||
|
# other values from the config, defined by the needs of env.py,
|
||||||
|
# can be acquired:
|
||||||
|
# my_important_option = config.get_main_option("my_important_option")
|
||||||
|
# ... etc.
|
||||||
|
|
||||||
|
|
||||||
|
def get_url():
|
||||||
|
# url = os.environ["DATABASE_URL"]
|
||||||
|
url = DevicehubConfig.SQLALCHEMY_DATABASE_URI
|
||||||
|
return url
|
||||||
|
|
||||||
|
|
||||||
|
def run_migrations_offline():
|
||||||
|
"""Run migrations in 'offline' mode.
|
||||||
|
|
||||||
|
This configures the context with just a URL
|
||||||
|
and not an Engine, though an Engine is acceptable
|
||||||
|
here as well. By skipping the Engine creation
|
||||||
|
we don't even need a DBAPI to be available.
|
||||||
|
|
||||||
|
Calls to context.execute() here emit the given string to the
|
||||||
|
script output.
|
||||||
|
|
||||||
|
"""
|
||||||
|
url = get_url()
|
||||||
|
context.configure(url=url, target_metadata=target_metadata, literal_binds=True)
|
||||||
|
|
||||||
|
with context.begin_transaction():
|
||||||
|
context.run_migrations()
|
||||||
|
|
||||||
|
|
||||||
|
def run_migrations_online():
|
||||||
|
"""Run migrations in 'online' mode.
|
||||||
|
|
||||||
|
In this scenario we need to create an Engine
|
||||||
|
and associate a connection with the context.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# connectable = engine_from_config(
|
||||||
|
# config.get_section(config.config_ini_section),
|
||||||
|
# prefix="sqlalchemy.",
|
||||||
|
# poolclass=pool.NullPool,
|
||||||
|
# )
|
||||||
|
|
||||||
|
url = get_url()
|
||||||
|
connectable = create_engine(url)
|
||||||
|
|
||||||
|
with connectable.connect() as connection:
|
||||||
|
context.configure(connection=connection, target_metadata=target_metadata)
|
||||||
|
|
||||||
|
with context.begin_transaction():
|
||||||
|
context.run_migrations()
|
||||||
|
|
||||||
|
|
||||||
|
if context.is_offline_mode():
|
||||||
|
run_migrations_offline()
|
||||||
|
else:
|
||||||
|
run_migrations_online()
|
33
ereuse_devicehub/migrations/script.py.mako
Normal file
33
ereuse_devicehub/migrations/script.py.mako
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
"""${message}
|
||||||
|
|
||||||
|
Revision ID: ${up_revision}
|
||||||
|
Revises: ${down_revision | comma,n}
|
||||||
|
Create Date: ${create_date}
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
import sqlalchemy_utils
|
||||||
|
import citext
|
||||||
|
import teal
|
||||||
|
${imports if imports else ""}
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = ${repr(up_revision)}
|
||||||
|
down_revision = ${repr(down_revision)}
|
||||||
|
branch_labels = ${repr(branch_labels)}
|
||||||
|
depends_on = ${repr(depends_on)}
|
||||||
|
|
||||||
|
|
||||||
|
def get_inv():
|
||||||
|
INV = context.get_x_argument(as_dictionary=True).get('inventory')
|
||||||
|
if not INV:
|
||||||
|
raise ValueError("Inventory value is not specified")
|
||||||
|
return INV
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
${upgrades if upgrades else "pass"}
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
${downgrades if downgrades else "pass"}
|
|
@ -0,0 +1,41 @@
|
||||||
|
"""Owner in tags
|
||||||
|
|
||||||
|
Revision ID: b9b0ee7d9dca
|
||||||
|
Revises: 151253ac5c55
|
||||||
|
Create Date: 2020-06-30 17:41:28.611314
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
from alembic import context
|
||||||
|
import sqlalchemy as sa
|
||||||
|
import sqlalchemy_utils
|
||||||
|
from sqlalchemy.dialects import postgresql
|
||||||
|
import citext
|
||||||
|
import teal
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'b9b0ee7d9dca'
|
||||||
|
down_revision = 'fbb7e2a0cde0'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_inv():
|
||||||
|
INV = context.get_x_argument(as_dictionary=True).get('inventory')
|
||||||
|
if not INV:
|
||||||
|
raise ValueError("Inventory value is not specified")
|
||||||
|
return INV
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.add_column('tag', sa.Column('owner_id', postgresql.UUID(), nullable=True), schema=f'{get_inv()}')
|
||||||
|
op.create_foreign_key("fk_tag_owner_id_user_id",
|
||||||
|
"tag", "user",
|
||||||
|
["owner_id"], ["id"],
|
||||||
|
ondelete="SET NULL",
|
||||||
|
source_schema=f'{get_inv()}', referent_schema='common')
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_constraint("fk_tag_owner_id_user_id", "tag", type_="foreignkey", schema=f'{get_inv()}')
|
||||||
|
op.drop_column('tag', 'owner_id', schema=f'{get_inv()}')
|
1159
ereuse_devicehub/migrations/versions/fbb7e2a0cde0_initial.py
Normal file
1159
ereuse_devicehub/migrations/versions/fbb7e2a0cde0_initial.py
Normal file
File diff suppressed because one or more lines are too long
|
@ -267,6 +267,7 @@ class MigrateFromDef(ActionDef):
|
||||||
VIEW = None
|
VIEW = None
|
||||||
SCHEMA = schemas.MigrateFrom
|
SCHEMA = schemas.MigrateFrom
|
||||||
|
|
||||||
|
|
||||||
class TransferredDef(ActionDef):
|
class TransferredDef(ActionDef):
|
||||||
VIEW = None
|
VIEW = None
|
||||||
SCHEMA = schemas.Transferred
|
SCHEMA = schemas.Transferred
|
||||||
|
|
|
@ -1423,6 +1423,7 @@ class DisposeProduct(Trade):
|
||||||
# performing :class:`.ToDispose` + :class:`.Receive` to a
|
# performing :class:`.ToDispose` + :class:`.Receive` to a
|
||||||
# ``RecyclingCenter``.
|
# ``RecyclingCenter``.
|
||||||
|
|
||||||
|
|
||||||
class TransferOwnershipBlockchain(Trade):
|
class TransferOwnershipBlockchain(Trade):
|
||||||
""" The act of change owenership of devices between two users (ethereum address)"""
|
""" The act of change owenership of devices between two users (ethereum address)"""
|
||||||
|
|
||||||
|
@ -1551,6 +1552,7 @@ def update_parent(target: Union[EraseBasic, Test, Install], device: Device, _, _
|
||||||
class InvalidRangeForPrice(ValueError):
|
class InvalidRangeForPrice(ValueError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Transferred(ActionWithMultipleDevices):
|
class Transferred(ActionWithMultipleDevices):
|
||||||
"""Transferred through blockchain."""
|
"""Transferred through blockchain."""
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -12,7 +12,7 @@ from ereuse_devicehub.resources.action.models import Action, RateComputer, Snaps
|
||||||
InitTransfer
|
InitTransfer
|
||||||
from ereuse_devicehub.resources.action.rate.v1_0 import CannotRate
|
from ereuse_devicehub.resources.action.rate.v1_0 import CannotRate
|
||||||
from ereuse_devicehub.resources.device.models import Component, Computer
|
from ereuse_devicehub.resources.device.models import Component, Computer
|
||||||
from ereuse_devicehub.resources.enums import SnapshotSoftware
|
from ereuse_devicehub.resources.enums import SnapshotSoftware, Severity
|
||||||
|
|
||||||
SUPPORTED_WORKBENCH = StrictVersion('11.0')
|
SUPPORTED_WORKBENCH = StrictVersion('11.0')
|
||||||
|
|
||||||
|
@ -98,8 +98,10 @@ class ActionView(View):
|
||||||
if price:
|
if price:
|
||||||
snapshot.actions.add(price)
|
snapshot.actions.add(price)
|
||||||
elif snapshot.software == SnapshotSoftware.WorkbenchAndroid:
|
elif snapshot.software == SnapshotSoftware.WorkbenchAndroid:
|
||||||
pass # TODO try except to compute RateMobile
|
pass # TODO try except to compute RateMobile
|
||||||
|
# Check if HID is null and add Severity:Warning to Snapshot
|
||||||
|
if snapshot.device.hid is None:
|
||||||
|
snapshot.severity = Severity.Warning
|
||||||
db.session.add(snapshot)
|
db.session.add(snapshot)
|
||||||
db.session().final_flush()
|
db.session().final_flush()
|
||||||
ret = self.schema.jsonify(snapshot) # transform it back
|
ret = self.schema.jsonify(snapshot) # transform it back
|
||||||
|
|
|
@ -4,7 +4,7 @@ from teal.resource import Converters, Resource
|
||||||
|
|
||||||
from ereuse_devicehub.resources.device import schemas
|
from ereuse_devicehub.resources.device import schemas
|
||||||
from ereuse_devicehub.resources.device.models import Manufacturer
|
from ereuse_devicehub.resources.device.models import Manufacturer
|
||||||
from ereuse_devicehub.resources.device.views import DeviceView, ManufacturerView
|
from ereuse_devicehub.resources.device.views import DeviceView, DeviceMergeView, ManufacturerView
|
||||||
|
|
||||||
|
|
||||||
class DeviceDef(Resource):
|
class DeviceDef(Resource):
|
||||||
|
@ -26,6 +26,13 @@ class DeviceDef(Resource):
|
||||||
super().__init__(app, import_name, static_folder, static_url_path, template_folder,
|
super().__init__(app, import_name, static_folder, static_url_path, template_folder,
|
||||||
url_prefix, subdomain, url_defaults, root_path, cli_commands)
|
url_prefix, subdomain, url_defaults, root_path, cli_commands)
|
||||||
|
|
||||||
|
device_merge = DeviceMergeView.as_view('merge-devices', definition=self, auth=app.auth)
|
||||||
|
if self.AUTH:
|
||||||
|
device_merge = app.auth.requires_auth(device_merge)
|
||||||
|
self.add_url_rule('/<{}:{}>/merge/'.format(self.ID_CONVERTER.value, self.ID_NAME),
|
||||||
|
view_func=device_merge,
|
||||||
|
methods={'POST'})
|
||||||
|
|
||||||
|
|
||||||
class ComputerDef(DeviceDef):
|
class ComputerDef(DeviceDef):
|
||||||
VIEW = None
|
VIEW = None
|
||||||
|
|
|
@ -52,7 +52,7 @@ class Device(Thing):
|
||||||
"""
|
"""
|
||||||
type = Column(Unicode(STR_SM_SIZE), nullable=False)
|
type = Column(Unicode(STR_SM_SIZE), nullable=False)
|
||||||
hid = Column(Unicode(), check_lower('hid'), unique=False)
|
hid = Column(Unicode(), check_lower('hid'), unique=False)
|
||||||
hid.comment = """The Hardware ID (HID) is the unique ID traceability
|
hid.comment = """The Hardware ID (HID) is the ID traceability
|
||||||
systems use to ID a device globally. This field is auto-generated
|
systems use to ID a device globally. This field is auto-generated
|
||||||
from Devicehub using literal identifiers from the device,
|
from Devicehub using literal identifiers from the device,
|
||||||
so it can re-generated *offline*.
|
so it can re-generated *offline*.
|
||||||
|
|
|
@ -12,7 +12,6 @@ from teal.marshmallow import ValidationError
|
||||||
|
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.resources.action.models import Remove
|
from ereuse_devicehub.resources.action.models import Remove
|
||||||
from ereuse_devicehub.resources.device.exceptions import NeedsId
|
|
||||||
from ereuse_devicehub.resources.device.models import Component, Computer, Device
|
from ereuse_devicehub.resources.device.models import Component, Computer, Device
|
||||||
from ereuse_devicehub.resources.tag.model import Tag
|
from ereuse_devicehub.resources.tag.model import Tag
|
||||||
|
|
||||||
|
@ -151,9 +150,6 @@ class Sync:
|
||||||
"""
|
"""
|
||||||
assert inspect(device).transient, 'Device cannot be already synced from DB'
|
assert inspect(device).transient, 'Device cannot be already synced from DB'
|
||||||
assert all(inspect(tag).transient for tag in device.tags), 'Tags cannot be synced from DB'
|
assert all(inspect(tag).transient for tag in device.tags), 'Tags cannot be synced from DB'
|
||||||
if not device.tags and not device.hid:
|
|
||||||
# We cannot identify this device
|
|
||||||
raise NeedsId()
|
|
||||||
db_device = None
|
db_device = None
|
||||||
if device.hid:
|
if device.hid:
|
||||||
with suppress(ResourceNotFound):
|
with suppress(ResourceNotFound):
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
import uuid
|
||||||
|
from itertools import filterfalse
|
||||||
|
|
||||||
import marshmallow
|
import marshmallow
|
||||||
from flask import g, current_app as app, render_template, request, Response
|
from flask import g, current_app as app, render_template, request, Response
|
||||||
from flask.json import jsonify
|
from flask.json import jsonify
|
||||||
from flask_sqlalchemy import Pagination
|
from flask_sqlalchemy import Pagination
|
||||||
from marshmallow import fields, fields as f, validate as v, ValidationError
|
from marshmallow import fields, fields as f, validate as v, ValidationError, \
|
||||||
|
Schema as MarshmallowSchema
|
||||||
from teal import query
|
from teal import query
|
||||||
from teal.cache import cache
|
from teal.cache import cache
|
||||||
from teal.resource import View
|
from teal.resource import View
|
||||||
|
@ -19,7 +22,7 @@ from ereuse_devicehub.resources.device.models import Device, Manufacturer, Compu
|
||||||
from ereuse_devicehub.resources.device.search import DeviceSearch
|
from ereuse_devicehub.resources.device.search import DeviceSearch
|
||||||
from ereuse_devicehub.resources.lot.models import LotDeviceDescendants
|
from ereuse_devicehub.resources.lot.models import LotDeviceDescendants
|
||||||
from ereuse_devicehub.resources.tag.model import Tag
|
from ereuse_devicehub.resources.tag.model import Tag
|
||||||
from ereuse_devicehub.resources.deliverynote.models import Deliverynote
|
from ereuse_devicehub.resources.enums import SnapshotSoftware
|
||||||
|
|
||||||
|
|
||||||
class OfType(f.Str):
|
class OfType(f.Str):
|
||||||
|
@ -142,7 +145,6 @@ class DeviceView(View):
|
||||||
|
|
||||||
def query(self, args):
|
def query(self, args):
|
||||||
query = Device.query.distinct() # todo we should not force to do this if the query is ok
|
query = Device.query.distinct() # todo we should not force to do this if the query is ok
|
||||||
|
|
||||||
search_p = args.get('search', None)
|
search_p = args.get('search', None)
|
||||||
if search_p:
|
if search_p:
|
||||||
properties = DeviceSearch.properties
|
properties = DeviceSearch.properties
|
||||||
|
@ -164,6 +166,67 @@ class DeviceView(View):
|
||||||
pass
|
pass
|
||||||
return query
|
return query
|
||||||
|
|
||||||
|
class DeviceMergeView(View):
|
||||||
|
|
||||||
|
"""View for merging two devices
|
||||||
|
Ex. ``device/<id>/merge/id=X``.
|
||||||
|
"""
|
||||||
|
class FindArgs(MarshmallowSchema):
|
||||||
|
id = fields.Integer()
|
||||||
|
|
||||||
|
def get_merge_id(self) -> uuid.UUID:
|
||||||
|
args = self.QUERY_PARSER.parse(self.find_args, request, locations=('querystring',))
|
||||||
|
return args['id']
|
||||||
|
|
||||||
|
def post(self, id: uuid.UUID):
|
||||||
|
device = Device.query.filter_by(id=id).one()
|
||||||
|
with_device = Device.query.filter_by(id=self.get_merge_id()).one()
|
||||||
|
self.merge_devices(device, with_device)
|
||||||
|
|
||||||
|
db.session().final_flush()
|
||||||
|
ret = self.schema.jsonify(device)
|
||||||
|
ret.status_code = 201
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def merge_devices(self, base_device, with_device):
|
||||||
|
"""Merge the current device with `with_device` by
|
||||||
|
adding all `with_device` actions under the current device.
|
||||||
|
|
||||||
|
This operation is highly costly as it forces refreshing
|
||||||
|
many models in session.
|
||||||
|
"""
|
||||||
|
snapshots = sorted(filterfalse(lambda x: not isinstance(x, actions.Snapshot), (base_device.actions + with_device.actions)))
|
||||||
|
workbench_snapshots = [ s for s in snapshots if s.software == (SnapshotSoftware.Workbench or SnapshotSoftware.WorkbenchAndroid)]
|
||||||
|
latest_snapshot_device = [ d for d in (base_device, with_device) if d.id == snapshots[-1].device.id][0]
|
||||||
|
latest_snapshotworkbench_device = [ d for d in (base_device, with_device) if d.id == workbench_snapshots[-1].device.id][0]
|
||||||
|
# Adding actions of with_device
|
||||||
|
with_actions_one = [a for a in with_device.actions if isinstance(a, actions.ActionWithOneDevice)]
|
||||||
|
with_actions_multiple = [a for a in with_device.actions if isinstance(a, actions.ActionWithMultipleDevices)]
|
||||||
|
|
||||||
|
for action in with_actions_one:
|
||||||
|
if action.parent:
|
||||||
|
action.parent = base_device
|
||||||
|
else:
|
||||||
|
base_device.actions_one.add(action)
|
||||||
|
for action in with_actions_multiple:
|
||||||
|
if action.parent:
|
||||||
|
action.parent = base_device
|
||||||
|
else:
|
||||||
|
base_device.actions_multiple.add(action)
|
||||||
|
|
||||||
|
# Keeping the components of latest SnapshotWorkbench
|
||||||
|
base_device.components = latest_snapshotworkbench_device.components
|
||||||
|
|
||||||
|
# Properties from latest Snapshot
|
||||||
|
base_device.type = latest_snapshot_device.type
|
||||||
|
base_device.hid = latest_snapshot_device.hid
|
||||||
|
base_device.manufacturer = latest_snapshot_device.manufacturer
|
||||||
|
base_device.model = latest_snapshot_device.model
|
||||||
|
base_device.chassis = latest_snapshot_device.chassis
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerView(View):
|
class ManufacturerView(View):
|
||||||
class FindArgs(marshmallow.Schema):
|
class FindArgs(marshmallow.Schema):
|
||||||
search = marshmallow.fields.Str(required=True,
|
search = marshmallow.fields.Str(required=True,
|
||||||
|
|
|
@ -43,7 +43,10 @@ class DeviceRow(OrderedDict):
|
||||||
self['Trading state'] = device.last_action_of(*states.Trading.actions()).t
|
self['Trading state'] = device.last_action_of(*states.Trading.actions()).t
|
||||||
except:
|
except:
|
||||||
self['Trading state'] = ''
|
self['Trading state'] = ''
|
||||||
self['Price'] = device.price.price or ''
|
try:
|
||||||
|
self['Price'] = device.price
|
||||||
|
except:
|
||||||
|
self['Price'] = ''
|
||||||
if isinstance(device, d.Computer):
|
if isinstance(device, d.Computer):
|
||||||
self['Processor'] = device.processor_model
|
self['Processor'] = device.processor_model
|
||||||
self['RAM (MB)'] = device.ram_size
|
self['RAM (MB)'] = device.ram_size
|
||||||
|
@ -73,7 +76,7 @@ class DeviceRow(OrderedDict):
|
||||||
# todo put an input specific order (non alphabetic) & where are a list of types components
|
# todo put an input specific order (non alphabetic) & where are a list of types components
|
||||||
for type in sorted(current_app.resources[d.Component.t].subresources_types): # type: str
|
for type in sorted(current_app.resources[d.Component.t].subresources_types): # type: str
|
||||||
max = self.NUMS.get(type, 4)
|
max = self.NUMS.get(type, 4)
|
||||||
if type not in ['Component', 'HardDrive', 'SolidStateDrive', 'Camera', 'Battery']:
|
if type not in ['Component', 'HardDrive', 'SolidStateDrive']:
|
||||||
i = 1
|
i = 1
|
||||||
for component in (r for r in self.device.components if r.type == type):
|
for component in (r for r in self.device.components if r.type == type):
|
||||||
self.fill_component(type, i, component)
|
self.fill_component(type, i, component)
|
||||||
|
|
|
@ -18,6 +18,7 @@ class TagDef(Resource):
|
||||||
VIEW = TagView
|
VIEW = TagView
|
||||||
ID_CONVERTER = Converters.lower
|
ID_CONVERTER = Converters.lower
|
||||||
|
|
||||||
|
OWNER_H = 'The id of the user who owns this tag. '
|
||||||
ORG_H = 'The name of an existing organization in the DB. '
|
ORG_H = 'The name of an existing organization in the DB. '
|
||||||
'By default the organization operating this Devicehub.'
|
'By default the organization operating this Devicehub.'
|
||||||
PROV_H = 'The Base URL of the provider; scheme + domain. Ex: "https://foo.com". '
|
PROV_H = 'The Base URL of the provider; scheme + domain. Ex: "https://foo.com". '
|
||||||
|
@ -48,6 +49,7 @@ class TagDef(Resource):
|
||||||
view_func=device_view,
|
view_func=device_view,
|
||||||
methods={'PUT'})
|
methods={'PUT'})
|
||||||
|
|
||||||
|
@option('-u', '--owner', help=OWNER_H)
|
||||||
@option('-o', '--org', help=ORG_H)
|
@option('-o', '--org', help=ORG_H)
|
||||||
@option('-p', '--provider', help=PROV_H)
|
@option('-p', '--provider', help=PROV_H)
|
||||||
@option('-s', '--sec', help=Tag.secondary.comment)
|
@option('-s', '--sec', help=Tag.secondary.comment)
|
||||||
|
@ -55,18 +57,19 @@ class TagDef(Resource):
|
||||||
def create_tag(self,
|
def create_tag(self,
|
||||||
id: str,
|
id: str,
|
||||||
org: str = None,
|
org: str = None,
|
||||||
|
owner: str = None,
|
||||||
sec: str = None,
|
sec: str = None,
|
||||||
provider: str = None):
|
provider: str = None):
|
||||||
"""Create a tag with the given ID."""
|
"""Create a tag with the given ID."""
|
||||||
db.session.add(Tag(**self.schema.load(
|
db.session.add(Tag(**self.schema.load(
|
||||||
dict(id=id, org=org, secondary=sec, provider=provider)
|
dict(id=id, owner=owner, org=org, secondary=sec, provider=provider)
|
||||||
)))
|
)))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
@option('--org', help=ORG_H)
|
@option('--org', help=ORG_H)
|
||||||
@option('--provider', help=PROV_H)
|
@option('--provider', help=PROV_H)
|
||||||
@argument('path', type=cli.Path(writable=True))
|
@argument('path', type=cli.Path(writable=True))
|
||||||
def create_tags_csv(self, path: pathlib.Path, org: str, provider: str):
|
def create_tags_csv(self, path: pathlib.Path, owner: str, org: str, provider: str):
|
||||||
"""Creates tags by reading CSV from ereuse-tag.
|
"""Creates tags by reading CSV from ereuse-tag.
|
||||||
|
|
||||||
CSV must have the following columns:
|
CSV must have the following columns:
|
||||||
|
@ -77,6 +80,6 @@ class TagDef(Resource):
|
||||||
with path.open() as f:
|
with path.open() as f:
|
||||||
for id, sec in csv.reader(f):
|
for id, sec in csv.reader(f):
|
||||||
db.session.add(Tag(**self.schema.load(
|
db.session.add(Tag(**self.schema.load(
|
||||||
dict(id=id, org=org, secondary=sec, provider=provider)
|
dict(id=id, owner=owner, org=org, secondary=sec, provider=provider)
|
||||||
)))
|
)))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from typing import Set
|
from typing import Set
|
||||||
|
|
||||||
|
from flask import g
|
||||||
from boltons import urlutils
|
from boltons import urlutils
|
||||||
from sqlalchemy import BigInteger, Column, ForeignKey, UniqueConstraint
|
from sqlalchemy import BigInteger, Column, ForeignKey, UniqueConstraint
|
||||||
from sqlalchemy.dialects.postgresql import UUID
|
from sqlalchemy.dialects.postgresql import UUID
|
||||||
|
@ -12,6 +13,7 @@ from teal.resource import url_for_resource
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.resources.agent.models import Organization
|
from ereuse_devicehub.resources.agent.models import Organization
|
||||||
from ereuse_devicehub.resources.device.models import Device
|
from ereuse_devicehub.resources.device.models import Device
|
||||||
|
from ereuse_devicehub.resources.user.models import User
|
||||||
from ereuse_devicehub.resources.models import Thing
|
from ereuse_devicehub.resources.models import Thing
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,6 +28,11 @@ class Tags(Set['Tag']):
|
||||||
class Tag(Thing):
|
class Tag(Thing):
|
||||||
id = Column(db.CIText(), primary_key=True)
|
id = Column(db.CIText(), primary_key=True)
|
||||||
id.comment = """The ID of the tag."""
|
id.comment = """The ID of the tag."""
|
||||||
|
owner_id = Column(UUID(as_uuid=True),
|
||||||
|
ForeignKey(User.id),
|
||||||
|
nullable=False,
|
||||||
|
default=lambda: g.user.id)
|
||||||
|
owner = relationship(User, primaryjoin=owner_id == User.id)
|
||||||
org_id = Column(UUID(as_uuid=True),
|
org_id = Column(UUID(as_uuid=True),
|
||||||
ForeignKey(Organization.id),
|
ForeignKey(Organization.id),
|
||||||
primary_key=True,
|
primary_key=True,
|
||||||
|
@ -50,7 +57,7 @@ class Tag(Thing):
|
||||||
primaryjoin=Device.id == device_id)
|
primaryjoin=Device.id == device_id)
|
||||||
"""The device linked to this tag."""
|
"""The device linked to this tag."""
|
||||||
secondary = Column(db.CIText(), index=True)
|
secondary = Column(db.CIText(), index=True)
|
||||||
secondary.comment = """A secondary identifier for this tag.
|
secondary.comment = """A secondary identifier for this tag.
|
||||||
It has the same constraints as the main one. Only needed in special cases.
|
It has the same constraints as the main one. Only needed in special cases.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ from sqlalchemy.util import OrderedSet
|
||||||
from teal.marshmallow import SanitizedStr, URL
|
from teal.marshmallow import SanitizedStr, URL
|
||||||
|
|
||||||
from ereuse_devicehub.marshmallow import NestedOn
|
from ereuse_devicehub.marshmallow import NestedOn
|
||||||
|
from ereuse_devicehub.resources.user.schemas import User
|
||||||
from ereuse_devicehub.resources.agent.schemas import Organization
|
from ereuse_devicehub.resources.agent.schemas import Organization
|
||||||
from ereuse_devicehub.resources.device.schemas import Device
|
from ereuse_devicehub.resources.device.schemas import Device
|
||||||
from ereuse_devicehub.resources.schemas import Thing
|
from ereuse_devicehub.resources.schemas import Thing
|
||||||
|
@ -22,6 +23,7 @@ class Tag(Thing):
|
||||||
provider = URL(description=m.Tag.provider.comment,
|
provider = URL(description=m.Tag.provider.comment,
|
||||||
validator=without_slash)
|
validator=without_slash)
|
||||||
device = NestedOn(Device, dump_only=True)
|
device = NestedOn(Device, dump_only=True)
|
||||||
|
owner = NestedOn(User, only_query='id')
|
||||||
org = NestedOn(Organization, collection_class=OrderedSet, only_query='id')
|
org = NestedOn(Organization, collection_class=OrderedSet, only_query='id')
|
||||||
secondary = SanitizedStr(lower=True, description=m.Tag.secondary.comment)
|
secondary = SanitizedStr(lower=True, description=m.Tag.secondary.comment)
|
||||||
printable = Boolean(dump_only=True, decsription=m.Tag.printable.__doc__)
|
printable = Boolean(dump_only=True, decsription=m.Tag.printable.__doc__)
|
||||||
|
|
|
@ -4,12 +4,14 @@ from teal.marshmallow import ValidationError
|
||||||
from teal.resource import View, url_for_resource
|
from teal.resource import View, url_for_resource
|
||||||
|
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
|
from ereuse_devicehub import auth
|
||||||
from ereuse_devicehub.query import things_response
|
from ereuse_devicehub.query import things_response
|
||||||
from ereuse_devicehub.resources.device.models import Device
|
from ereuse_devicehub.resources.device.models import Device
|
||||||
from ereuse_devicehub.resources.tag import Tag
|
from ereuse_devicehub.resources.tag import Tag
|
||||||
|
|
||||||
|
|
||||||
class TagView(View):
|
class TagView(View):
|
||||||
|
@auth.Auth.requires_auth
|
||||||
def post(self):
|
def post(self):
|
||||||
"""Creates a tag."""
|
"""Creates a tag."""
|
||||||
num = request.args.get('num', type=int)
|
num = request.args.get('num', type=int)
|
||||||
|
@ -19,8 +21,10 @@ class TagView(View):
|
||||||
res = self._post_one()
|
res = self._post_one()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
@auth.Auth.requires_auth
|
||||||
def find(self, args: dict):
|
def find(self, args: dict):
|
||||||
tags = Tag.query.filter(Tag.is_printable_q()) \
|
tags = Tag.query.filter(Tag.is_printable_q()) \
|
||||||
|
.filter_by(owner=g.user) \
|
||||||
.order_by(Tag.created.desc()) \
|
.order_by(Tag.created.desc()) \
|
||||||
.paginate(per_page=200) # type: Pagination
|
.paginate(per_page=200) # type: Pagination
|
||||||
return things_response(
|
return things_response(
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
alembic==1.4.2
|
||||||
anytree==2.4.3
|
anytree==2.4.3
|
||||||
apispec==0.39.0
|
apispec==0.39.0
|
||||||
boltons==18.0.1
|
boltons==18.0.1
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
Type,Chassis,Tag 1,Tag 2,Tag 3,Serial Number,Model,Manufacturer,Registered in,Price,Processor,RAM (GB),Data Storage Size (MB),Rate,Range,Processor Rate,Processor Range,RAM Rate,RAM Range,Data Storage Rate,Data Storage Range,Battery 1,Battery 1 Manufacturer,Battery 1 Model,Battery 1 Serial Number,Battery 2,Battery 2 Manufacturer,Battery 2 Model,Battery 2 Serial Number,Battery 3,Battery 3 Manufacturer,Battery 3 Model,Battery 3 Serial Number,Battery 4,Battery 4 Manufacturer,Battery 4 Model,Battery 4 Serial Number,Camera 1,Camera 1 Manufacturer,Camera 1 Model,Camera 1 Serial Number,Camera 2,Camera 2 Manufacturer,Camera 2 Model,Camera 2 Serial Number,Camera 3,Camera 3 Manufacturer,Camera 3 Model,Camera 3 Serial Number,Camera 4,Camera 4 Manufacturer,Camera 4 Model,Camera 4 Serial Number,DataStorage 1,DataStorage 1 Manufacturer,DataStorage 1 Model,DataStorage 1 Serial Number,DataStorage 2,DataStorage 2 Manufacturer,DataStorage 2 Model,DataStorage 2 Serial Number,DataStorage 3,DataStorage 3 Manufacturer,DataStorage 3 Model,DataStorage 3 Serial Number,DataStorage 4,DataStorage 4 Manufacturer,DataStorage 4 Model,DataStorage 4 Serial Number,Display 1,Display 1 Manufacturer,Display 1 Model,Display 1 Serial Number,GraphicCard 1,GraphicCard 1 Manufacturer,GraphicCard 1 Model,GraphicCard 1 Serial Number,GraphicCard 1 Memory (MB),GraphicCard 2,GraphicCard 2 Manufacturer,GraphicCard 2 Model,GraphicCard 2 Serial Number,Motherboard 1,Motherboard 1 Manufacturer,Motherboard 1 Model,Motherboard 1 Serial Number,NetworkAdapter 1,NetworkAdapter 1 Manufacturer,NetworkAdapter 1 Model,NetworkAdapter 1 Serial Number,NetworkAdapter 2,NetworkAdapter 2 Manufacturer,NetworkAdapter 2 Model,NetworkAdapter 2 Serial Number,Processor 1,Processor 1 Manufacturer,Processor 1 Model,Processor 1 Serial Number,Processor 1 Number of cores,Processor 1 Speed (GHz),Processor 2,Processor 2 Manufacturer,Processor 2 Model,Processor 2 Serial Number,RamModule 1,RamModule 1 Manufacturer,RamModule 1 Model,RamModule 1 Serial Number,RamModule 1 Size (MB),RamModule 1 Speed (MHz),RamModule 2,RamModule 2 Manufacturer,RamModule 2 Model,RamModule 2 Serial Number,RamModule 3,RamModule 3 Manufacturer,RamModule 3 Model,RamModule 3 Serial Number,RamModule 4,RamModule 4 Manufacturer,RamModule 4 Model,RamModule 4 Serial Number,SoundCard 1,SoundCard 1 Manufacturer,SoundCard 1 Model,SoundCard 1 Serial Number,SoundCard 2,SoundCard 2 Manufacturer,SoundCard 2 Model,SoundCard 2 Serial Number
|
Type,Chassis,Tag 1,Tag 2,Tag 3,Serial Number,Model,Manufacturer,Registered in,Physical state,Trading state,Price,Processor,RAM (MB),Data Storage Size (MB),Rate,Range,Processor Rate,Processor Range,RAM Rate,RAM Range,Data Storage Rate,Data Storage Range,Battery 1,Battery 1 Manufacturer,Battery 1 Model,Battery 1 Serial Number,Battery 2,Battery 2 Manufacturer,Battery 2 Model,Battery 2 Serial Number,Battery 3,Battery 3 Manufacturer,Battery 3 Model,Battery 3 Serial Number,Battery 4,Battery 4 Manufacturer,Battery 4 Model,Battery 4 Serial Number,Camera 1,Camera 1 Manufacturer,Camera 1 Model,Camera 1 Serial Number,Camera 2,Camera 2 Manufacturer,Camera 2 Model,Camera 2 Serial Number,Camera 3,Camera 3 Manufacturer,Camera 3 Model,Camera 3 Serial Number,Camera 4,Camera 4 Manufacturer,Camera 4 Model,Camera 4 Serial Number,DataStorage 1,DataStorage 1 Manufacturer,DataStorage 1 Model,DataStorage 1 Serial Number,DataStorage 2,DataStorage 2 Manufacturer,DataStorage 2 Model,DataStorage 2 Serial Number,DataStorage 3,DataStorage 3 Manufacturer,DataStorage 3 Model,DataStorage 3 Serial Number,DataStorage 4,DataStorage 4 Manufacturer,DataStorage 4 Model,DataStorage 4 Serial Number,Display 1,Display 1 Manufacturer,Display 1 Model,Display 1 Serial Number,GraphicCard 1,GraphicCard 1 Manufacturer,GraphicCard 1 Model,GraphicCard 1 Serial Number,GraphicCard 1 Memory (MB),GraphicCard 2,GraphicCard 2 Manufacturer,GraphicCard 2 Model,GraphicCard 2 Serial Number,Motherboard 1,Motherboard 1 Manufacturer,Motherboard 1 Model,Motherboard 1 Serial Number,NetworkAdapter 1,NetworkAdapter 1 Manufacturer,NetworkAdapter 1 Model,NetworkAdapter 1 Serial Number,NetworkAdapter 2,NetworkAdapter 2 Manufacturer,NetworkAdapter 2 Model,NetworkAdapter 2 Serial Number,Processor 1,Processor 1 Manufacturer,Processor 1 Model,Processor 1 Serial Number,Processor 1 Number of cores,Processor 1 Speed (GHz),Processor 2,Processor 2 Manufacturer,Processor 2 Model,Processor 2 Serial Number,RamModule 1,RamModule 1 Manufacturer,RamModule 1 Model,RamModule 1 Serial Number,RamModule 1 Size (MB),RamModule 1 Speed (MHz),RamModule 2,RamModule 2 Manufacturer,RamModule 2 Model,RamModule 2 Serial Number,RamModule 3,RamModule 3 Manufacturer,RamModule 3 Model,RamModule 3 Serial Number,RamModule 4,RamModule 4 Manufacturer,RamModule 4 Model,RamModule 4 Serial Number,SoundCard 1,SoundCard 1 Manufacturer,SoundCard 1 Model,SoundCard 1 Serial Number,SoundCard 2,SoundCard 2 Manufacturer,SoundCard 2 Model,SoundCard 2 Serial Number
|
||||||
Desktop,Microtower,,,,d1s,d1ml,d1mr,Tue Jul 2 10:35:10 2019,,p1ml,0,0,0.8,Very low,1.0,Very low,1.0,Very low,1.0,Very low,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"GraphicCard 2: model gc1ml, S/N gc1s",gc1s,gc1s,gc1s,,,,,,,,,,,,,,,,,,"Processor 4: model p1ml, S/N p1s",p1s,p1s,p1s,,1.6,,,,,"RamModule 3: model rm1ml, S/N rm1s",rm1s,rm1s,rm1s,,1333,,,,,,,,,,,,,,,,,,,,
|
Desktop,Microtower,,,,d1s,d1ml,d1mr,Tue Jul 2 10:35:10 2019,,,,p1ml,0,0,1.0,Very low,1.0,Very low,1.0,Very low,1.0,Very low,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"GraphicCard 2: model gc1ml, S/N gc1s",gc1s,gc1s,gc1s,,,,,,,,,,,,,,,,,,"Processor 4: model p1ml, S/N p1s",p1s,p1s,p1s,,1.6,,,,,"RamModule 3: model rm1ml, S/N rm1s",rm1s,rm1s,rm1s,,1333,,,,,,,,,,,,,,,,,,,,
|
||||||
|
|
|
|
@ -1,2 +1,2 @@
|
||||||
Type,Chassis,Tag 1,Tag 2,Tag 3,Serial Number,Model,Manufacturer,Registered in,Price
|
Type,Chassis,Tag 1,Tag 2,Tag 3,Serial Number,Model,Manufacturer,Registered in,Physical state,Trading state,Price
|
||||||
ComputerMonitor,,,,,cn0fp446728728541c8s,1707fpf,dell,Wed Oct 24 20:57:18 2018
|
ComputerMonitor,,,,,cn0fp446728728541c8s,1707fpf,dell,Wed Oct 24 20:57:18 2018
|
|
|
@ -1,2 +1,2 @@
|
||||||
Type,Chassis,Tag 1,Tag 2,Tag 3,Serial Number,Model,Manufacturer,Registered in,Price
|
Type,Chassis,Tag 1,Tag 2,Tag 3,Serial Number,Model,Manufacturer,Registered in,Physical state,Trading state,Price
|
||||||
Keyboard,,,,,bar,foo,baz,Wed Oct 24 21:01:48 2018
|
Keyboard,,,,,bar,foo,baz,Wed Oct 24 21:01:48 2018
|
||||||
|
|
|
|
@ -1,5 +1,5 @@
|
||||||
Type,Chassis,Tag 1,Tag 2,Tag 3,Serial Number,Model,Manufacturer,Registered in,Price,Processor,RAM (GB),Data Storage Size (MB),Rate,Range,Processor Rate,Processor Range,RAM Rate,RAM Range,Data Storage Rate,Data Storage Range,Battery 1,Battery 1 Manufacturer,Battery 1 Model,Battery 1 Serial Number,Battery 2,Battery 2 Manufacturer,Battery 2 Model,Battery 2 Serial Number,Battery 3,Battery 3 Manufacturer,Battery 3 Model,Battery 3 Serial Number,Battery 4,Battery 4 Manufacturer,Battery 4 Model,Battery 4 Serial Number,Camera 1,Camera 1 Manufacturer,Camera 1 Model,Camera 1 Serial Number,Camera 2,Camera 2 Manufacturer,Camera 2 Model,Camera 2 Serial Number,Camera 3,Camera 3 Manufacturer,Camera 3 Model,Camera 3 Serial Number,Camera 4,Camera 4 Manufacturer,Camera 4 Model,Camera 4 Serial Number,DataStorage 1,DataStorage 1 Manufacturer,DataStorage 1 Model,DataStorage 1 Serial Number,DataStorage 2,DataStorage 2 Manufacturer,DataStorage 2 Model,DataStorage 2 Serial Number,DataStorage 3,DataStorage 3 Manufacturer,DataStorage 3 Model,DataStorage 3 Serial Number,DataStorage 4,DataStorage 4 Manufacturer,DataStorage 4 Model,DataStorage 4 Serial Number,Display 1,Display 1 Manufacturer,Display 1 Model,Display 1 Serial Number,GraphicCard 1,GraphicCard 1 Manufacturer,GraphicCard 1 Model,GraphicCard 1 Serial Number,GraphicCard 1 Memory (MB),GraphicCard 2,GraphicCard 2 Manufacturer,GraphicCard 2 Model,GraphicCard 2 Serial Number,Motherboard 1,Motherboard 1 Manufacturer,Motherboard 1 Model,Motherboard 1 Serial Number,NetworkAdapter 1,NetworkAdapter 1 Manufacturer,NetworkAdapter 1 Model,NetworkAdapter 1 Serial Number,NetworkAdapter 2,NetworkAdapter 2 Manufacturer,NetworkAdapter 2 Model,NetworkAdapter 2 Serial Number,Processor 1,Processor 1 Manufacturer,Processor 1 Model,Processor 1 Serial Number,Processor 1 Number of cores,Processor 1 Speed (GHz),Processor 2,Processor 2 Manufacturer,Processor 2 Model,Processor 2 Serial Number,RamModule 1,RamModule 1 Manufacturer,RamModule 1 Model,RamModule 1 Serial Number,RamModule 1 Size (MB),RamModule 1 Speed (MHz),RamModule 2,RamModule 2 Manufacturer,RamModule 2 Model,RamModule 2 Serial Number,RamModule 3,RamModule 3 Manufacturer,RamModule 3 Model,RamModule 3 Serial Number,RamModule 4,RamModule 4 Manufacturer,RamModule 4 Model,RamModule 4 Serial Number,SoundCard 1,SoundCard 1 Manufacturer,SoundCard 1 Model,SoundCard 1 Serial Number,SoundCard 2,SoundCard 2 Manufacturer,SoundCard 2 Model,SoundCard 2 Serial Number
|
Type,Chassis,Tag 1,Tag 2,Tag 3,Serial Number,Model,Manufacturer,Registered in,Physical state,Trading state,Price,Processor,RAM (MB),Data Storage Size (MB),Rate,Range,Processor Rate,Processor Range,RAM Rate,RAM Range,Data Storage Rate,Data Storage Range,Battery 1,Battery 1 Manufacturer,Battery 1 Model,Battery 1 Serial Number,Battery 2,Battery 2 Manufacturer,Battery 2 Model,Battery 2 Serial Number,Battery 3,Battery 3 Manufacturer,Battery 3 Model,Battery 3 Serial Number,Battery 4,Battery 4 Manufacturer,Battery 4 Model,Battery 4 Serial Number,Camera 1,Camera 1 Manufacturer,Camera 1 Model,Camera 1 Serial Number,Camera 2,Camera 2 Manufacturer,Camera 2 Model,Camera 2 Serial Number,Camera 3,Camera 3 Manufacturer,Camera 3 Model,Camera 3 Serial Number,Camera 4,Camera 4 Manufacturer,Camera 4 Model,Camera 4 Serial Number,DataStorage 1,DataStorage 1 Manufacturer,DataStorage 1 Model,DataStorage 1 Serial Number,DataStorage 2,DataStorage 2 Manufacturer,DataStorage 2 Model,DataStorage 2 Serial Number,DataStorage 3,DataStorage 3 Manufacturer,DataStorage 3 Model,DataStorage 3 Serial Number,DataStorage 4,DataStorage 4 Manufacturer,DataStorage 4 Model,DataStorage 4 Serial Number,Display 1,Display 1 Manufacturer,Display 1 Model,Display 1 Serial Number,GraphicCard 1,GraphicCard 1 Manufacturer,GraphicCard 1 Model,GraphicCard 1 Serial Number,GraphicCard 1 Memory (MB),GraphicCard 2,GraphicCard 2 Manufacturer,GraphicCard 2 Model,GraphicCard 2 Serial Number,Motherboard 1,Motherboard 1 Manufacturer,Motherboard 1 Model,Motherboard 1 Serial Number,NetworkAdapter 1,NetworkAdapter 1 Manufacturer,NetworkAdapter 1 Model,NetworkAdapter 1 Serial Number,NetworkAdapter 2,NetworkAdapter 2 Manufacturer,NetworkAdapter 2 Model,NetworkAdapter 2 Serial Number,Processor 1,Processor 1 Manufacturer,Processor 1 Model,Processor 1 Serial Number,Processor 1 Number of cores,Processor 1 Speed (GHz),Processor 2,Processor 2 Manufacturer,Processor 2 Model,Processor 2 Serial Number,RamModule 1,RamModule 1 Manufacturer,RamModule 1 Model,RamModule 1 Serial Number,RamModule 1 Size (MB),RamModule 1 Speed (MHz),RamModule 2,RamModule 2 Manufacturer,RamModule 2 Model,RamModule 2 Serial Number,RamModule 3,RamModule 3 Manufacturer,RamModule 3 Model,RamModule 3 Serial Number,RamModule 4,RamModule 4 Manufacturer,RamModule 4 Model,RamModule 4 Serial Number,SoundCard 1,SoundCard 1 Manufacturer,SoundCard 1 Model,SoundCard 1 Serial Number,SoundCard 2,SoundCard 2 Manufacturer,SoundCard 2 Model,SoundCard 2 Serial Number
|
||||||
Laptop,Netbook,,,,b8oaas048286,1001pxd,asustek computer inc.,Tue Jul 2 10:38:14 2019,,intel atom cpu n455 @ 1.66ghz,1024,238475,1.98,Very low,1.31,Very low,1.53,Very low,3.76,Medium,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"GraphicCard 5: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None",,,,256,,,,,"Motherboard 10: model 1001pxd, S/N eee0123456789",eee0123456789,eee0123456789,eee0123456789,"NetworkAdapter 2: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c8",74:2f:68:8b:fd:c8,74:2f:68:8b:fd:c8,74:2f:68:8b:fd:c8,"NetworkAdapter 3: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7c",14:da:e9:42:f6:7c,14:da:e9:42:f6:7c,14:da:e9:42:f6:7c,"Processor 4: model intel atom cpu n455 @ 1.66ghz, S/N None",,,,1,1.667,,,,,"RamModule 8: model None, S/N None",,,,1024,667,,,,,,,,,,,,,"SoundCard 6: model nm10/ich7 family high definition audio controller, S/N None",,,,"SoundCard 7: model usb 2.0 uvc vga webcam, S/N 0x0001",0x0001,0x0001,0x0001
|
Laptop,Netbook,,,,b8oaas048286,1001pxd,asustek computer inc.,Tue Jul 2 10:37:44 2019,,,47.40 €,intel atom cpu n455 @ 1.66ghz,1024,238475,1.58,Low,1.31,Low,1.53,Low,3.76,High,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"GraphicCard 5: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None",,,,256,,,,,"Motherboard 10: model 1001pxd, S/N eee0123456789",eee0123456789,eee0123456789,eee0123456789,"NetworkAdapter 2: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c8",74:2f:68:8b:fd:c8,74:2f:68:8b:fd:c8,74:2f:68:8b:fd:c8,"NetworkAdapter 3: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7c",14:da:e9:42:f6:7c,14:da:e9:42:f6:7c,14:da:e9:42:f6:7c,"Processor 4: model intel atom cpu n455 @ 1.66ghz, S/N None",,,,1,1.667,,,,,"RamModule 8: model None, S/N None",,,,1024,667,,,,,,,,,,,,,"SoundCard 6: model nm10/ich7 family high definition audio controller, S/N None",,,,"SoundCard 7: model usb 2.0 uvc vga webcam, S/N 0x0001",0x0001,0x0001,0x0001
|
||||||
Desktop,Microtower,,,,d1s,d1ml,d1mr,Tue Jul 2 10:38:14 2019,,p1ml,0,0,0.8,Very low,1.0,Very low,1.0,Very low,1.0,Very low,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"GraphicCard 12: model gc1ml, S/N gc1s",gc1s,gc1s,gc1s,,,,,,,,,,,,,,,,,,"Processor 14: model p1ml, S/N p1s",p1s,p1s,p1s,,1.6,,,,,"RamModule 13: model rm1ml, S/N rm1s",rm1s,rm1s,rm1s,,1333,,,,,,,,,,,,,,,,,,,,
|
Desktop,Microtower,,,,d1s,d1ml,d1mr,Tue Jul 2 10:38:14 2019,,,,p1ml,0,0,1.0,Very low,1.0,Very low,1.0,Very low,1.0,Very low,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"GraphicCard 12: model gc1ml, S/N gc1s",gc1s,gc1s,gc1s,,,,,,,,,,,,,,,,,,"Processor 14: model p1ml, S/N p1s",p1s,p1s,p1s,,1.6,,,,,"RamModule 13: model rm1ml, S/N rm1s",rm1s,rm1s,rm1s,,1333,,,,,,,,,,,,,,,,,,,,
|
||||||
Keyboard,,,,,bar,foo,baz,Tue Jul 2 10:38:14 2019,
|
Keyboard,,,,,bar,foo,baz,Tue Jul 2 10:38:14 2019,,,
|
||||||
ComputerMonitor,,,,,cn0fp446728728541c8s,1707fpf,dell,Tue Jul 2 10:38:15 2019,
|
ComputerMonitor,,,,,cn0fp446728728541c8s,1707fpf,dell,Tue Jul 2 10:38:15 2019,,,
|
||||||
|
|
Can't render this file because it has a wrong number of fields in line 4.
|
|
@ -1,2 +1,2 @@
|
||||||
Type,Chassis,Tag 1,Tag 2,Tag 3,Serial Number,Model,Manufacturer,Registered in,Price,Processor,RAM (GB),Data Storage Size (MB),Rate,Range,Processor Rate,Processor Range,RAM Rate,RAM Range,Data Storage Rate,Data Storage Range,Battery 1,Battery 1 Manufacturer,Battery 1 Model,Battery 1 Serial Number,Battery 2,Battery 2 Manufacturer,Battery 2 Model,Battery 2 Serial Number,Battery 3,Battery 3 Manufacturer,Battery 3 Model,Battery 3 Serial Number,Battery 4,Battery 4 Manufacturer,Battery 4 Model,Battery 4 Serial Number,Camera 1,Camera 1 Manufacturer,Camera 1 Model,Camera 1 Serial Number,Camera 2,Camera 2 Manufacturer,Camera 2 Model,Camera 2 Serial Number,Camera 3,Camera 3 Manufacturer,Camera 3 Model,Camera 3 Serial Number,Camera 4,Camera 4 Manufacturer,Camera 4 Model,Camera 4 Serial Number,DataStorage 1,DataStorage 1 Manufacturer,DataStorage 1 Model,DataStorage 1 Serial Number,DataStorage 2,DataStorage 2 Manufacturer,DataStorage 2 Model,DataStorage 2 Serial Number,DataStorage 3,DataStorage 3 Manufacturer,DataStorage 3 Model,DataStorage 3 Serial Number,DataStorage 4,DataStorage 4 Manufacturer,DataStorage 4 Model,DataStorage 4 Serial Number,Display 1,Display 1 Manufacturer,Display 1 Model,Display 1 Serial Number,GraphicCard 1,GraphicCard 1 Manufacturer,GraphicCard 1 Model,GraphicCard 1 Serial Number,GraphicCard 1 Memory (MB),GraphicCard 2,GraphicCard 2 Manufacturer,GraphicCard 2 Model,GraphicCard 2 Serial Number,Motherboard 1,Motherboard 1 Manufacturer,Motherboard 1 Model,Motherboard 1 Serial Number,NetworkAdapter 1,NetworkAdapter 1 Manufacturer,NetworkAdapter 1 Model,NetworkAdapter 1 Serial Number,NetworkAdapter 2,NetworkAdapter 2 Manufacturer,NetworkAdapter 2 Model,NetworkAdapter 2 Serial Number,Processor 1,Processor 1 Manufacturer,Processor 1 Model,Processor 1 Serial Number,Processor 1 Number of cores,Processor 1 Speed (GHz),Processor 2,Processor 2 Manufacturer,Processor 2 Model,Processor 2 Serial Number,RamModule 1,RamModule 1 Manufacturer,RamModule 1 Model,RamModule 1 Serial Number,RamModule 1 Size (MB),RamModule 1 Speed (MHz),RamModule 2,RamModule 2 Manufacturer,RamModule 2 Model,RamModule 2 Serial Number,RamModule 3,RamModule 3 Manufacturer,RamModule 3 Model,RamModule 3 Serial Number,RamModule 4,RamModule 4 Manufacturer,RamModule 4 Model,RamModule 4 Serial Number,SoundCard 1,SoundCard 1 Manufacturer,SoundCard 1 Model,SoundCard 1 Serial Number,SoundCard 2,SoundCard 2 Manufacturer,SoundCard 2 Model,SoundCard 2 Serial Number
|
Type,Chassis,Tag 1,Tag 2,Tag 3,Serial Number,Model,Manufacturer,Registered in,Physical state,Trading state,Price,Processor,RAM (MB),Data Storage Size (MB),Rate,Range,Processor Rate,Processor Range,RAM Rate,RAM Range,Data Storage Rate,Data Storage Range,Battery 1,Battery 1 Manufacturer,Battery 1 Model,Battery 1 Serial Number,Battery 2,Battery 2 Manufacturer,Battery 2 Model,Battery 2 Serial Number,Battery 3,Battery 3 Manufacturer,Battery 3 Model,Battery 3 Serial Number,Battery 4,Battery 4 Manufacturer,Battery 4 Model,Battery 4 Serial Number,Camera 1,Camera 1 Manufacturer,Camera 1 Model,Camera 1 Serial Number,Camera 2,Camera 2 Manufacturer,Camera 2 Model,Camera 2 Serial Number,Camera 3,Camera 3 Manufacturer,Camera 3 Model,Camera 3 Serial Number,Camera 4,Camera 4 Manufacturer,Camera 4 Model,Camera 4 Serial Number,DataStorage 1,DataStorage 1 Manufacturer,DataStorage 1 Model,DataStorage 1 Serial Number,DataStorage 2,DataStorage 2 Manufacturer,DataStorage 2 Model,DataStorage 2 Serial Number,DataStorage 3,DataStorage 3 Manufacturer,DataStorage 3 Model,DataStorage 3 Serial Number,DataStorage 4,DataStorage 4 Manufacturer,DataStorage 4 Model,DataStorage 4 Serial Number,Display 1,Display 1 Manufacturer,Display 1 Model,Display 1 Serial Number,GraphicCard 1,GraphicCard 1 Manufacturer,GraphicCard 1 Model,GraphicCard 1 Serial Number,GraphicCard 1 Memory (MB),GraphicCard 2,GraphicCard 2 Manufacturer,GraphicCard 2 Model,GraphicCard 2 Serial Number,Motherboard 1,Motherboard 1 Manufacturer,Motherboard 1 Model,Motherboard 1 Serial Number,NetworkAdapter 1,NetworkAdapter 1 Manufacturer,NetworkAdapter 1 Model,NetworkAdapter 1 Serial Number,NetworkAdapter 2,NetworkAdapter 2 Manufacturer,NetworkAdapter 2 Model,NetworkAdapter 2 Serial Number,Processor 1,Processor 1 Manufacturer,Processor 1 Model,Processor 1 Serial Number,Processor 1 Number of cores,Processor 1 Speed (GHz),Processor 2,Processor 2 Manufacturer,Processor 2 Model,Processor 2 Serial Number,RamModule 1,RamModule 1 Manufacturer,RamModule 1 Model,RamModule 1 Serial Number,RamModule 1 Size (MB),RamModule 1 Speed (MHz),RamModule 2,RamModule 2 Manufacturer,RamModule 2 Model,RamModule 2 Serial Number,RamModule 3,RamModule 3 Manufacturer,RamModule 3 Model,RamModule 3 Serial Number,RamModule 4,RamModule 4 Manufacturer,RamModule 4 Model,RamModule 4 Serial Number,SoundCard 1,SoundCard 1 Manufacturer,SoundCard 1 Model,SoundCard 1 Serial Number,SoundCard 2,SoundCard 2 Manufacturer,SoundCard 2 Model,SoundCard 2 Serial Number
|
||||||
Laptop,Netbook,,,,b8oaas048286,1001pxd,asustek computer inc.,Tue Jul 2 10:37:44 2019,,intel atom cpu n455 @ 1.66ghz,1024,238475,1.98,Very low,1.31,Very low,1.53,Very low,3.76,Medium,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"GraphicCard 5: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None",,,,256,,,,,"Motherboard 10: model 1001pxd, S/N eee0123456789",eee0123456789,eee0123456789,eee0123456789,"NetworkAdapter 2: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c8",74:2f:68:8b:fd:c8,74:2f:68:8b:fd:c8,74:2f:68:8b:fd:c8,"NetworkAdapter 3: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7c",14:da:e9:42:f6:7c,14:da:e9:42:f6:7c,14:da:e9:42:f6:7c,"Processor 4: model intel atom cpu n455 @ 1.66ghz, S/N None",,,,1,1.667,,,,,"RamModule 8: model None, S/N None",,,,1024,667,,,,,,,,,,,,,"SoundCard 6: model nm10/ich7 family high definition audio controller, S/N None",,,,"SoundCard 7: model usb 2.0 uvc vga webcam, S/N 0x0001",0x0001,0x0001,0x0001
|
Laptop,Netbook,,,,b8oaas048286,1001pxd,asustek computer inc.,Tue Jul 2 10:37:44 2019,,,47.40 €,intel atom cpu n455 @ 1.66ghz,1024,238475,1.58,Low,1.31,Low,1.53,Low,3.76,High,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"GraphicCard 5: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None",,,,256,,,,,"Motherboard 10: model 1001pxd, S/N eee0123456789",eee0123456789,eee0123456789,eee0123456789,"NetworkAdapter 2: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c8",74:2f:68:8b:fd:c8,74:2f:68:8b:fd:c8,74:2f:68:8b:fd:c8,"NetworkAdapter 3: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7c",14:da:e9:42:f6:7c,14:da:e9:42:f6:7c,14:da:e9:42:f6:7c,"Processor 4: model intel atom cpu n455 @ 1.66ghz, S/N None",,,,1,1.667,,,,,"RamModule 8: model None, S/N None",,,,1024,667,,,,,,,,,,,,,"SoundCard 6: model nm10/ich7 family high definition audio controller, S/N None",,,,"SoundCard 7: model usb 2.0 uvc vga webcam, S/N 0x0001",0x0001,0x0001,0x0001
|
||||||
|
|
|
|
@ -20,6 +20,7 @@ from tests import conftest
|
||||||
from tests.conftest import create_user, file
|
from tests.conftest import create_user, file
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_author():
|
def test_author():
|
||||||
"""Checks the default created author.
|
"""Checks the default created author.
|
||||||
|
@ -36,6 +37,7 @@ def test_author():
|
||||||
assert e.author == user
|
assert e.author == user
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_erase_basic():
|
def test_erase_basic():
|
||||||
erasure = models.EraseBasic(
|
erasure = models.EraseBasic(
|
||||||
|
@ -54,6 +56,7 @@ def test_erase_basic():
|
||||||
assert not erasure.standards, 'EraseBasic themselves do not have standards'
|
assert not erasure.standards, 'EraseBasic themselves do not have standards'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_validate_device_data_storage():
|
def test_validate_device_data_storage():
|
||||||
"""Checks the validation for data-storage-only actions works."""
|
"""Checks the validation for data-storage-only actions works."""
|
||||||
|
@ -68,6 +71,7 @@ def test_validate_device_data_storage():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_erase_sectors_steps_erasure_standards_hmg_is5():
|
def test_erase_sectors_steps_erasure_standards_hmg_is5():
|
||||||
erasure = models.EraseSectors(
|
erasure = models.EraseSectors(
|
||||||
|
@ -89,6 +93,7 @@ def test_erase_sectors_steps_erasure_standards_hmg_is5():
|
||||||
assert {enums.ErasureStandards.HMG_IS5} == erasure.standards
|
assert {enums.ErasureStandards.HMG_IS5} == erasure.standards
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_test_data_storage_working():
|
def test_test_data_storage_working():
|
||||||
"""Tests TestDataStorage with the resulting properties in Device."""
|
"""Tests TestDataStorage with the resulting properties in Device."""
|
||||||
|
@ -121,6 +126,7 @@ def test_test_data_storage_working():
|
||||||
assert hdd.problems == []
|
assert hdd.problems == []
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_install():
|
def test_install():
|
||||||
hdd = HardDrive(serial_number='sn')
|
hdd = HardDrive(serial_number='sn')
|
||||||
|
@ -131,6 +137,7 @@ def test_install():
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_update_components_action_one():
|
def test_update_components_action_one():
|
||||||
computer = Desktop(serial_number='sn1',
|
computer = Desktop(serial_number='sn1',
|
||||||
|
@ -159,6 +166,7 @@ def test_update_components_action_one():
|
||||||
assert len(test.components) == 1
|
assert len(test.components) == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_update_components_action_multiple():
|
def test_update_components_action_multiple():
|
||||||
computer = Desktop(serial_number='sn1',
|
computer = Desktop(serial_number='sn1',
|
||||||
|
@ -188,6 +196,7 @@ def test_update_components_action_multiple():
|
||||||
assert ready.components
|
assert ready.components
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_update_parent():
|
def test_update_parent():
|
||||||
computer = Desktop(serial_number='sn1',
|
computer = Desktop(serial_number='sn1',
|
||||||
|
@ -208,6 +217,7 @@ def test_update_parent():
|
||||||
assert not benchmark.parent
|
assert not benchmark.parent
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.parametrize('action_model_state',
|
@pytest.mark.parametrize('action_model_state',
|
||||||
(pytest.param(ams, id=ams[0].__class__.__name__)
|
(pytest.param(ams, id=ams[0].__class__.__name__)
|
||||||
for ams in [
|
for ams in [
|
||||||
|
@ -230,6 +240,7 @@ def test_generic_action(action_model_state: Tuple[models.Action, states.Trading]
|
||||||
assert device['physical'] == state.name
|
assert device['physical'] == state.name
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_live():
|
def test_live():
|
||||||
"""Tests inserting a Live into the database and GETting it."""
|
"""Tests inserting a Live into the database and GETting it."""
|
||||||
|
@ -255,18 +266,7 @@ def test_live():
|
||||||
assert device['physical'] == states.Physical.InUse.name
|
assert device['physical'] == states.Physical.InUse.name
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reson='Functionality not developed.')
|
@pytest.mark.mvp
|
||||||
def test_live_geoip():
|
|
||||||
"""Tests performing a Live action using the GEOIP library."""
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reson='Develop reserve')
|
|
||||||
def test_reserve_and_cancel(user: UserClient):
|
|
||||||
"""Performs a reservation and then cancels it,
|
|
||||||
checking the attribute `reservees`.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('action_model_state',
|
@pytest.mark.parametrize('action_model_state',
|
||||||
(pytest.param(ams, id=ams[0].__name__)
|
(pytest.param(ams, id=ams[0].__name__)
|
||||||
for ams in [
|
for ams in [
|
||||||
|
@ -296,11 +296,7 @@ def test_trade(action_model_state: Tuple[Type[models.Action], states.Trading], u
|
||||||
assert device['trading'] == state.name
|
assert device['trading'] == state.name
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reson='Develop migrate')
|
@pytest.mark.mvp
|
||||||
def test_migrate():
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_price_custom():
|
def test_price_custom():
|
||||||
computer = Desktop(serial_number='sn1', model='ml1', manufacturer='mr1',
|
computer = Desktop(serial_number='sn1', model='ml1', manufacturer='mr1',
|
||||||
|
@ -322,6 +318,7 @@ def test_price_custom():
|
||||||
assert c['price']['id'] == p['id']
|
assert c['price']['id'] == p['id']
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_price_custom_client(user: UserClient):
|
def test_price_custom_client(user: UserClient):
|
||||||
"""As test_price_custom but creating the price through the API."""
|
"""As test_price_custom but creating the price through the API."""
|
||||||
s = file('basic.snapshot')
|
s = file('basic.snapshot')
|
||||||
|
@ -339,16 +336,7 @@ def test_price_custom_client(user: UserClient):
|
||||||
assert 25 == device['price']['price']
|
assert 25 == device['price']['price']
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reson='Develop test')
|
@pytest.mark.mvp
|
||||||
def test_ereuse_price():
|
|
||||||
"""Tests the several ways of creating eReuse Price, emulating
|
|
||||||
from an AggregateRate and ensuring that the different Range
|
|
||||||
return correct results.
|
|
||||||
"""
|
|
||||||
# important to check Range.low no returning warranty2
|
|
||||||
# Range.verylow not returning nothing
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_erase_physical():
|
def test_erase_physical():
|
||||||
erasure = models.ErasePhysical(
|
erasure = models.ErasePhysical(
|
||||||
|
@ -357,27 +345,3 @@ def test_erase_physical():
|
||||||
)
|
)
|
||||||
db.session.add(erasure)
|
db.session.add(erasure)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reson='develop')
|
|
||||||
def test_measure_battery():
|
|
||||||
"""Tests the MeasureBattery."""
|
|
||||||
# todo jn
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reson='develop')
|
|
||||||
def test_test_camera():
|
|
||||||
"""Tests the TestCamera."""
|
|
||||||
# todo jn
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reson='develop')
|
|
||||||
def test_test_keyboard():
|
|
||||||
"""Tests the TestKeyboard."""
|
|
||||||
# todo jn
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reson='develop')
|
|
||||||
def test_test_trackpad():
|
|
||||||
"""Tests the TestTrackpad."""
|
|
||||||
# todo jn
|
|
|
@ -8,6 +8,7 @@ from ereuse_devicehub.devicehub import Devicehub
|
||||||
from tests.conftest import create_user
|
from tests.conftest import create_user
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_authenticate_success(app: Devicehub):
|
def test_authenticate_success(app: Devicehub):
|
||||||
"""Checks the authenticate method."""
|
"""Checks the authenticate method."""
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
|
@ -16,6 +17,7 @@ def test_authenticate_success(app: Devicehub):
|
||||||
assert response_user == user
|
assert response_user == user
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_authenticate_error(app: Devicehub):
|
def test_authenticate_error(app: Devicehub):
|
||||||
"""Tests the authenticate method with wrong token values."""
|
"""Tests the authenticate method with wrong token values."""
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
|
@ -29,6 +31,7 @@ def test_authenticate_error(app: Devicehub):
|
||||||
app.auth.authenticate(token='this is a wrong uuid')
|
app.auth.authenticate(token='this is a wrong uuid')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_auth_view(user: UserClient, client: Client):
|
def test_auth_view(user: UserClient, client: Client):
|
||||||
"""Tests authentication at endpoint / view."""
|
"""Tests authentication at endpoint / view."""
|
||||||
user.get(res='User', item=user.user['id'], status=200)
|
user.get(res='User', item=user.user['id'], status=200)
|
||||||
|
|
|
@ -1,8 +1,19 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from ereuse_devicehub.devicehub import Devicehub
|
||||||
from ereuse_devicehub.client import Client
|
from ereuse_devicehub.client import Client
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
def test_dummy(_app: Devicehub):
|
||||||
|
"""Tests the dummy cli command."""
|
||||||
|
runner = _app.test_cli_runner()
|
||||||
|
runner.invoke('dummy', '--yes')
|
||||||
|
with _app.app_context():
|
||||||
|
_app.db.drop_all()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_dependencies():
|
def test_dependencies():
|
||||||
with pytest.raises(ImportError):
|
with pytest.raises(ImportError):
|
||||||
# Simplejson has a different signature than stdlib json
|
# Simplejson has a different signature than stdlib json
|
||||||
|
@ -12,6 +23,7 @@ def test_dependencies():
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyArgumentList
|
# noinspection PyArgumentList
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_api_docs(client: Client):
|
def test_api_docs(client: Client):
|
||||||
"""Tests /apidocs correct initialization."""
|
"""Tests /apidocs correct initialization."""
|
||||||
docs, _ = client.get('/apidocs')
|
docs, _ = client.get('/apidocs')
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import datetime
|
import datetime
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
|
import pytest
|
||||||
from teal.db import UniqueViolation
|
from teal.db import UniqueViolation
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_unique_violation():
|
def test_unique_violation():
|
||||||
class IntegrityErrorMock:
|
class IntegrityErrorMock:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import datetime
|
import datetime
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
from flask import g
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from colour import Color
|
from colour import Color
|
||||||
|
@ -22,14 +23,15 @@ from ereuse_devicehub.resources.device.schemas import Device as DeviceS
|
||||||
from ereuse_devicehub.resources.device.sync import MismatchBetweenTags, MismatchBetweenTagsAndHid, \
|
from ereuse_devicehub.resources.device.sync import MismatchBetweenTags, MismatchBetweenTagsAndHid, \
|
||||||
Sync
|
Sync
|
||||||
from ereuse_devicehub.resources.enums import ComputerChassis, DisplayTech, Severity, \
|
from ereuse_devicehub.resources.enums import ComputerChassis, DisplayTech, Severity, \
|
||||||
SnapshotSoftware
|
SnapshotSoftware, TransferState
|
||||||
from ereuse_devicehub.resources.tag.model import Tag
|
from ereuse_devicehub.resources.tag.model import Tag
|
||||||
from ereuse_devicehub.resources.user import User
|
from ereuse_devicehub.resources.user import User
|
||||||
from tests import conftest
|
from tests import conftest
|
||||||
from tests.conftest import file
|
from tests.conftest import file
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_device_model():
|
def test_device_model():
|
||||||
"""Tests that the correctness of the device model and its relationships."""
|
"""Tests that the correctness of the device model and its relationships."""
|
||||||
pc = d.Desktop(model='p1mo',
|
pc = d.Desktop(model='p1mo',
|
||||||
|
@ -76,6 +78,7 @@ def test_device_problems():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_device_schema():
|
def test_device_schema():
|
||||||
"""Ensures the user does not upload non-writable or extra fields."""
|
"""Ensures the user does not upload non-writable or extra fields."""
|
||||||
|
@ -84,7 +87,8 @@ def test_device_schema():
|
||||||
device_s.dump(d.Device(id=1))
|
device_s.dump(d.Device(id=1))
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_physical_properties():
|
def test_physical_properties():
|
||||||
c = d.Motherboard(slots=2,
|
c = d.Motherboard(slots=2,
|
||||||
usb=3,
|
usb=3,
|
||||||
|
@ -118,14 +122,21 @@ def test_physical_properties():
|
||||||
'ram_slots': None
|
'ram_slots': None
|
||||||
}
|
}
|
||||||
assert pc.physical_properties == {
|
assert pc.physical_properties == {
|
||||||
'model': 'foo',
|
'chassis': ComputerChassis.Tower,
|
||||||
|
'deliverynote_address': None,
|
||||||
|
'deposit': 0,
|
||||||
|
'ethereum_address': None,
|
||||||
'manufacturer': 'bar',
|
'manufacturer': 'bar',
|
||||||
|
'model': 'foo',
|
||||||
|
'owner_id': pc.owner_id,
|
||||||
|
'receiver_id': None,
|
||||||
'serial_number': 'foo-bar',
|
'serial_number': 'foo-bar',
|
||||||
'chassis': ComputerChassis.Tower
|
'transfer_state': TransferState.Initial
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_component_similar_one():
|
def test_component_similar_one():
|
||||||
snapshot = conftest.file('pc-components.db')
|
snapshot = conftest.file('pc-components.db')
|
||||||
pc = snapshot['device']
|
pc = snapshot['device']
|
||||||
|
@ -147,7 +158,8 @@ def test_component_similar_one():
|
||||||
assert componentA.similar_one(pc, blacklist={componentA.id})
|
assert componentA.similar_one(pc, blacklist={componentA.id})
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('auth_app_context')
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_add_remove():
|
def test_add_remove():
|
||||||
# Original state:
|
# Original state:
|
||||||
# pc has c1 and c2
|
# pc has c1 and c2
|
||||||
|
@ -178,7 +190,8 @@ def test_add_remove():
|
||||||
assert actions[0].components == OrderedSet([c3])
|
assert actions[0].components == OrderedSet([c3])
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_sync_run_components_empty():
|
def test_sync_run_components_empty():
|
||||||
"""Syncs a device that has an empty components list. The system should
|
"""Syncs a device that has an empty components list. The system should
|
||||||
remove all the components from the device.
|
remove all the components from the device.
|
||||||
|
@ -195,7 +208,8 @@ def test_sync_run_components_empty():
|
||||||
assert not pc.components
|
assert not pc.components
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_sync_run_components_none():
|
def test_sync_run_components_none():
|
||||||
"""Syncs a device that has a None components. The system should
|
"""Syncs a device that has a None components. The system should
|
||||||
keep all the components from the device.
|
keep all the components from the device.
|
||||||
|
@ -212,7 +226,8 @@ def test_sync_run_components_none():
|
||||||
assert db_pc.components == pc.components
|
assert db_pc.components == pc.components
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_sync_execute_register_desktop_new_desktop_no_tag():
|
def test_sync_execute_register_desktop_new_desktop_no_tag():
|
||||||
"""Syncs a new d.Desktop with HID and without a tag, creating it."""
|
"""Syncs a new d.Desktop with HID and without a tag, creating it."""
|
||||||
# Case 1: device does not exist on DB
|
# Case 1: device does not exist on DB
|
||||||
|
@ -221,7 +236,8 @@ def test_sync_execute_register_desktop_new_desktop_no_tag():
|
||||||
assert pc.physical_properties == db_pc.physical_properties
|
assert pc.physical_properties == db_pc.physical_properties
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_sync_execute_register_desktop_existing_no_tag():
|
def test_sync_execute_register_desktop_existing_no_tag():
|
||||||
"""Syncs an existing d.Desktop with HID and without a tag."""
|
"""Syncs an existing d.Desktop with HID and without a tag."""
|
||||||
pc = d.Desktop(**conftest.file('pc-components.db')['device'])
|
pc = d.Desktop(**conftest.file('pc-components.db')['device'])
|
||||||
|
@ -232,9 +248,13 @@ def test_sync_execute_register_desktop_existing_no_tag():
|
||||||
**conftest.file('pc-components.db')['device']) # Create a new transient non-db object
|
**conftest.file('pc-components.db')['device']) # Create a new transient non-db object
|
||||||
# 1: device exists on DB
|
# 1: device exists on DB
|
||||||
db_pc = Sync().execute_register(pc)
|
db_pc = Sync().execute_register(pc)
|
||||||
|
pc.deposit = 0
|
||||||
|
pc.owner_id = db_pc.owner_id
|
||||||
|
pc.transfer_state = TransferState.Initial
|
||||||
assert pc.physical_properties == db_pc.physical_properties
|
assert pc.physical_properties == db_pc.physical_properties
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_sync_execute_register_desktop_no_hid_no_tag():
|
def test_sync_execute_register_desktop_no_hid_no_tag():
|
||||||
"""Syncs a d.Desktop without HID and no tag.
|
"""Syncs a d.Desktop without HID and no tag.
|
||||||
|
@ -248,7 +268,8 @@ def test_sync_execute_register_desktop_no_hid_no_tag():
|
||||||
Sync().execute_register(pc)
|
Sync().execute_register(pc)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_sync_execute_register_desktop_tag_not_linked():
|
def test_sync_execute_register_desktop_tag_not_linked():
|
||||||
"""Syncs a new d.Desktop with HID and a non-linked tag.
|
"""Syncs a new d.Desktop with HID and a non-linked tag.
|
||||||
|
|
||||||
|
@ -266,7 +287,8 @@ def test_sync_execute_register_desktop_tag_not_linked():
|
||||||
assert d.Desktop.query.one() == pc, 'd.Desktop had to be set to db'
|
assert d.Desktop.query.one() == pc, 'd.Desktop had to be set to db'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_sync_execute_register_no_hid_tag_not_linked(tag_id: str):
|
def test_sync_execute_register_no_hid_tag_not_linked(tag_id: str):
|
||||||
"""Validates registering a d.Desktop without HID and a non-linked tag.
|
"""Validates registering a d.Desktop without HID and a non-linked tag.
|
||||||
|
|
||||||
|
@ -276,6 +298,7 @@ def test_sync_execute_register_no_hid_tag_not_linked(tag_id: str):
|
||||||
"""
|
"""
|
||||||
tag = Tag(id=tag_id)
|
tag = Tag(id=tag_id)
|
||||||
pc = d.Desktop(**conftest.file('pc-components.db')['device'], tags=OrderedSet([tag]))
|
pc = d.Desktop(**conftest.file('pc-components.db')['device'], tags=OrderedSet([tag]))
|
||||||
|
db.session.add(g.user)
|
||||||
returned_pc = Sync().execute_register(pc)
|
returned_pc = Sync().execute_register(pc)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
assert returned_pc == pc
|
assert returned_pc == pc
|
||||||
|
@ -288,6 +311,7 @@ def test_sync_execute_register_no_hid_tag_not_linked(tag_id: str):
|
||||||
assert d.Desktop.query.one() == pc, 'd.Desktop had to be set to db'
|
assert d.Desktop.query.one() == pc, 'd.Desktop had to be set to db'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_sync_execute_register_tag_does_not_exist():
|
def test_sync_execute_register_tag_does_not_exist():
|
||||||
"""Ensures not being able to register if the tag does not exist,
|
"""Ensures not being able to register if the tag does not exist,
|
||||||
|
@ -300,7 +324,8 @@ def test_sync_execute_register_tag_does_not_exist():
|
||||||
Sync().execute_register(pc)
|
Sync().execute_register(pc)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_sync_execute_register_tag_linked_same_device():
|
def test_sync_execute_register_tag_linked_same_device():
|
||||||
"""If the tag is linked to the device, regardless if it has HID,
|
"""If the tag is linked to the device, regardless if it has HID,
|
||||||
the system should match the device through the tag.
|
the system should match the device through the tag.
|
||||||
|
@ -320,7 +345,8 @@ def test_sync_execute_register_tag_linked_same_device():
|
||||||
assert next(iter(db_pc.tags)).id == 'foo'
|
assert next(iter(db_pc.tags)).id == 'foo'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_sync_execute_register_tag_linked_other_device_mismatch_between_tags():
|
def test_sync_execute_register_tag_linked_other_device_mismatch_between_tags():
|
||||||
"""Checks that sync raises an error if finds that at least two passed-in
|
"""Checks that sync raises an error if finds that at least two passed-in
|
||||||
tags are not linked to the same device.
|
tags are not linked to the same device.
|
||||||
|
@ -341,7 +367,8 @@ def test_sync_execute_register_tag_linked_other_device_mismatch_between_tags():
|
||||||
Sync().execute_register(pc1)
|
Sync().execute_register(pc1)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_sync_execute_register_mismatch_between_tags_and_hid():
|
def test_sync_execute_register_mismatch_between_tags_and_hid():
|
||||||
"""Checks that sync raises an error if it finds that the HID does
|
"""Checks that sync raises an error if it finds that the HID does
|
||||||
not point at the same device as the tag does.
|
not point at the same device as the tag does.
|
||||||
|
@ -363,6 +390,8 @@ def test_sync_execute_register_mismatch_between_tags_and_hid():
|
||||||
Sync().execute_register(pc1)
|
Sync().execute_register(pc1)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.xfail(reason='It needs to be fixed.')
|
||||||
def test_get_device(app: Devicehub, user: UserClient):
|
def test_get_device(app: Devicehub, user: UserClient):
|
||||||
"""Checks GETting a d.Desktop with its components."""
|
"""Checks GETting a d.Desktop with its components."""
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
|
@ -398,6 +427,8 @@ def test_get_device(app: Devicehub, user: UserClient):
|
||||||
assert pc['type'] == d.Desktop.t
|
assert pc['type'] == d.Desktop.t
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.xfail(reason='It needs to be fixed.')
|
||||||
def test_get_devices(app: Devicehub, user: UserClient):
|
def test_get_devices(app: Devicehub, user: UserClient):
|
||||||
"""Checks GETting multiple devices."""
|
"""Checks GETting multiple devices."""
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
|
@ -426,7 +457,8 @@ def test_get_devices(app: Devicehub, user: UserClient):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_computer_monitor():
|
def test_computer_monitor():
|
||||||
m = d.ComputerMonitor(technology=DisplayTech.LCD,
|
m = d.ComputerMonitor(technology=DisplayTech.LCD,
|
||||||
manufacturer='foo',
|
manufacturer='foo',
|
||||||
|
@ -439,6 +471,7 @@ def test_computer_monitor():
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_manufacturer(user: UserClient):
|
def test_manufacturer(user: UserClient):
|
||||||
m, r = user.get(res='Manufacturer', query=[('search', 'asus')])
|
m, r = user.get(res='Manufacturer', query=[('search', 'asus')])
|
||||||
assert m == {'items': [{'name': 'Asus', 'url': 'https://en.wikipedia.org/wiki/Asus'}]}
|
assert m == {'items': [{'name': 'Asus', 'url': 'https://en.wikipedia.org/wiki/Asus'}]}
|
||||||
|
@ -446,6 +479,7 @@ def test_manufacturer(user: UserClient):
|
||||||
assert r.expires > datetime.datetime.now()
|
assert r.expires > datetime.datetime.now()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.xfail(reason='Develop functionality')
|
@pytest.mark.xfail(reason='Develop functionality')
|
||||||
def test_manufacturer_enforced():
|
def test_manufacturer_enforced():
|
||||||
"""Ensures that non-computer devices can submit only
|
"""Ensures that non-computer devices can submit only
|
||||||
|
@ -453,6 +487,7 @@ def test_manufacturer_enforced():
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_device_properties_format(app: Devicehub, user: UserClient):
|
def test_device_properties_format(app: Devicehub, user: UserClient):
|
||||||
user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot)
|
user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot)
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
|
@ -475,6 +510,7 @@ def test_device_properties_format(app: Devicehub, user: UserClient):
|
||||||
assert format(hdd, 's') == 'seagate 5SV4TQA6 – 152 GB'
|
assert format(hdd, 's') == 'seagate 5SV4TQA6 – 152 GB'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_device_public(user: UserClient, client: Client):
|
def test_device_public(user: UserClient, client: Client):
|
||||||
s, _ = user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot)
|
s, _ = user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot)
|
||||||
html, _ = client.get(res=d.Device, item=s['device']['id'], accept=ANY)
|
html, _ = client.get(res=d.Device, item=s['device']['id'], accept=ANY)
|
||||||
|
@ -482,6 +518,7 @@ def test_device_public(user: UserClient, client: Client):
|
||||||
assert '00:24:8C:7F:CF:2D – 100 Mbps' in html
|
assert '00:24:8C:7F:CF:2D – 100 Mbps' in html
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_computer_accessory_model():
|
def test_computer_accessory_model():
|
||||||
sai = d.SAI()
|
sai = d.SAI()
|
||||||
|
@ -493,6 +530,7 @@ def test_computer_accessory_model():
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_networking_model():
|
def test_networking_model():
|
||||||
router = d.Router(speed=1000, wireless=True)
|
router = d.Router(speed=1000, wireless=True)
|
||||||
|
|
|
@ -15,6 +15,7 @@ from tests import conftest
|
||||||
from tests.conftest import file
|
from tests.conftest import file
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_device_filters():
|
def test_device_filters():
|
||||||
schema = Filters()
|
schema = Filters()
|
||||||
|
@ -171,6 +172,7 @@ def test_device_query_filter_lots(user: UserClient):
|
||||||
), 'Adding both lots is redundant in this case and we have the 4 elements.'
|
), 'Adding both lots is redundant in this case and we have the 4 elements.'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_device_query(user: UserClient):
|
def test_device_query(user: UserClient):
|
||||||
"""Checks result of inventory."""
|
"""Checks result of inventory."""
|
||||||
user.post(conftest.file('basic.snapshot'), res=Snapshot)
|
user.post(conftest.file('basic.snapshot'), res=Snapshot)
|
||||||
|
@ -183,6 +185,7 @@ def test_device_query(user: UserClient):
|
||||||
assert not pc['tags']
|
assert not pc['tags']
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_device_search_all_devices_token_if_empty(app: Devicehub, user: UserClient):
|
def test_device_search_all_devices_token_if_empty(app: Devicehub, user: UserClient):
|
||||||
"""Ensures DeviceSearch can regenerate itself when the table is empty."""
|
"""Ensures DeviceSearch can regenerate itself when the table is empty."""
|
||||||
user.post(file('basic.snapshot'), res=Snapshot)
|
user.post(file('basic.snapshot'), res=Snapshot)
|
||||||
|
@ -198,6 +201,7 @@ def test_device_search_all_devices_token_if_empty(app: Devicehub, user: UserClie
|
||||||
assert i['items']
|
assert i['items']
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_device_search_regenerate_table(app: DeviceSearch, user: UserClient):
|
def test_device_search_regenerate_table(app: DeviceSearch, user: UserClient):
|
||||||
user.post(file('basic.snapshot'), res=Snapshot)
|
user.post(file('basic.snapshot'), res=Snapshot)
|
||||||
i, _ = user.get(res=Device, query=[('search', 'Desktop')])
|
i, _ = user.get(res=Device, query=[('search', 'Desktop')])
|
||||||
|
@ -213,6 +217,7 @@ def test_device_search_regenerate_table(app: DeviceSearch, user: UserClient):
|
||||||
assert i['items'], 'Regenerated re-made the table'
|
assert i['items'], 'Regenerated re-made the table'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_device_query_search(user: UserClient):
|
def test_device_query_search(user: UserClient):
|
||||||
# todo improve
|
# todo improve
|
||||||
user.post(file('basic.snapshot'), res=Snapshot)
|
user.post(file('basic.snapshot'), res=Snapshot)
|
||||||
|
@ -226,6 +231,7 @@ def test_device_query_search(user: UserClient):
|
||||||
assert len(i['items']) == 1
|
assert len(i['items']) == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_device_query_search_synonyms_asus(user: UserClient):
|
def test_device_query_search_synonyms_asus(user: UserClient):
|
||||||
user.post(file('real-eee-1001pxd.snapshot.11'), res=Snapshot)
|
user.post(file('real-eee-1001pxd.snapshot.11'), res=Snapshot)
|
||||||
i, _ = user.get(res=Device, query=[('search', 'asustek')])
|
i, _ = user.get(res=Device, query=[('search', 'asustek')])
|
||||||
|
@ -234,6 +240,7 @@ def test_device_query_search_synonyms_asus(user: UserClient):
|
||||||
assert 1 == len(i['items'])
|
assert 1 == len(i['items'])
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_device_query_search_synonyms_intel(user: UserClient):
|
def test_device_query_search_synonyms_intel(user: UserClient):
|
||||||
s = file('real-hp.snapshot.11')
|
s = file('real-hp.snapshot.11')
|
||||||
s['device']['model'] = 'foo' # The model had the word 'HP' in it
|
s['device']['model'] = 'foo' # The model had the word 'HP' in it
|
||||||
|
|
|
@ -11,12 +11,14 @@ def noop():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def dispatcher(app: Devicehub, config: TestConfig) -> PathDispatcher:
|
def dispatcher(app: Devicehub, config: TestConfig) -> PathDispatcher:
|
||||||
PathDispatcher.call = Mock(side_effect=lambda *args: args[0])
|
PathDispatcher.call = Mock(side_effect=lambda *args: args[0])
|
||||||
return PathDispatcher(config_cls=config)
|
return PathDispatcher(config_cls=config)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_dispatcher_default(dispatcher: PathDispatcher):
|
def test_dispatcher_default(dispatcher: PathDispatcher):
|
||||||
"""The dispatcher returns not found for an URL that does not
|
"""The dispatcher returns not found for an URL that does not
|
||||||
route to an app.
|
route to an app.
|
||||||
|
@ -27,6 +29,7 @@ def test_dispatcher_default(dispatcher: PathDispatcher):
|
||||||
assert app == PathDispatcher.NOT_FOUND
|
assert app == PathDispatcher.NOT_FOUND
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_dispatcher_return_app(dispatcher: PathDispatcher):
|
def test_dispatcher_return_app(dispatcher: PathDispatcher):
|
||||||
"""The dispatcher returns the correct app for the URL."""
|
"""The dispatcher returns the correct app for the URL."""
|
||||||
# Note that the dispatcher does not check if the URL points
|
# Note that the dispatcher does not check if the URL points
|
||||||
|
@ -38,6 +41,7 @@ def test_dispatcher_return_app(dispatcher: PathDispatcher):
|
||||||
assert app.id == 'test'
|
assert app.id == 'test'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_dispatcher_users(dispatcher: PathDispatcher):
|
def test_dispatcher_users(dispatcher: PathDispatcher):
|
||||||
"""Users special endpoint returns an app."""
|
"""Users special endpoint returns an app."""
|
||||||
# For now returns the first app, as all apps
|
# For now returns the first app, as all apps
|
||||||
|
|
|
@ -1,25 +1,31 @@
|
||||||
|
import pytest
|
||||||
import teal.marshmallow
|
import teal.marshmallow
|
||||||
from ereuse_utils.test import ANY
|
from ereuse_utils.test import ANY
|
||||||
|
import csv
|
||||||
|
from datetime import datetime
|
||||||
|
from io import StringIO
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from ereuse_devicehub.client import Client, UserClient
|
from ereuse_devicehub.client import Client, UserClient
|
||||||
from ereuse_devicehub.resources.action import models as e
|
from ereuse_devicehub.resources.action.models import Snapshot
|
||||||
from ereuse_devicehub.resources.documents import documents as docs
|
from ereuse_devicehub.resources.documents import documents
|
||||||
from tests.conftest import file
|
from tests.conftest import file
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_erasure_certificate_public_one(user: UserClient, client: Client):
|
def test_erasure_certificate_public_one(user: UserClient, client: Client):
|
||||||
"""Public user can get certificate from one device as HTML or PDF."""
|
"""Public user can get certificate from one device as HTML or PDF."""
|
||||||
s = file('erase-sectors.snapshot')
|
s = file('erase-sectors.snapshot')
|
||||||
snapshot, _ = user.post(s, res=e.Snapshot)
|
snapshot, _ = user.post(s, res=Snapshot)
|
||||||
|
|
||||||
doc, response = client.get(res=docs.DocumentDef.t,
|
doc, response = client.get(res=documents.DocumentDef.t,
|
||||||
item='erasures/{}'.format(snapshot['device']['id']),
|
item='erasures/{}'.format(snapshot['device']['id']),
|
||||||
accept=ANY)
|
accept=ANY)
|
||||||
assert 'html' in response.content_type
|
assert 'html' in response.content_type
|
||||||
assert '<html' in doc
|
assert '<html' in doc
|
||||||
assert '2018' in doc
|
assert '2018' in doc
|
||||||
|
|
||||||
doc, response = client.get(res=docs.DocumentDef.t,
|
doc, response = client.get(res=documents.DocumentDef.t,
|
||||||
item='erasures/{}'.format(snapshot['device']['id']),
|
item='erasures/{}'.format(snapshot['device']['id']),
|
||||||
query=[('format', 'PDF')],
|
query=[('format', 'PDF')],
|
||||||
accept='application/pdf')
|
accept='application/pdf')
|
||||||
|
@ -27,7 +33,7 @@ def test_erasure_certificate_public_one(user: UserClient, client: Client):
|
||||||
|
|
||||||
erasure = next(e for e in snapshot['actions'] if e['type'] == 'EraseSectors')
|
erasure = next(e for e in snapshot['actions'] if e['type'] == 'EraseSectors')
|
||||||
|
|
||||||
doc, response = client.get(res=docs.DocumentDef.t,
|
doc, response = client.get(res=documents.DocumentDef.t,
|
||||||
item='erasures/{}'.format(erasure['id']),
|
item='erasures/{}'.format(erasure['id']),
|
||||||
accept=ANY)
|
accept=ANY)
|
||||||
assert 'html' in response.content_type
|
assert 'html' in response.content_type
|
||||||
|
@ -35,14 +41,15 @@ def test_erasure_certificate_public_one(user: UserClient, client: Client):
|
||||||
assert '2018' in doc
|
assert '2018' in doc
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_erasure_certificate_private_query(user: UserClient):
|
def test_erasure_certificate_private_query(user: UserClient):
|
||||||
"""Logged-in user can get certificates using queries as HTML and
|
"""Logged-in user can get certificates using queries as HTML and
|
||||||
PDF.
|
PDF.
|
||||||
"""
|
"""
|
||||||
s = file('erase-sectors.snapshot')
|
s = file('erase-sectors.snapshot')
|
||||||
snapshot, response = user.post(s, res=e.Snapshot)
|
snapshot, response = user.post(s, res=Snapshot)
|
||||||
|
|
||||||
doc, response = user.get(res=docs.DocumentDef.t,
|
doc, response = user.get(res=documents.DocumentDef.t,
|
||||||
item='erasures/',
|
item='erasures/',
|
||||||
query=[('filter', {'id': [snapshot['device']['id']]})],
|
query=[('filter', {'id': [snapshot['device']['id']]})],
|
||||||
accept=ANY)
|
accept=ANY)
|
||||||
|
@ -50,7 +57,7 @@ def test_erasure_certificate_private_query(user: UserClient):
|
||||||
assert '<html' in doc
|
assert '<html' in doc
|
||||||
assert '2018' in doc
|
assert '2018' in doc
|
||||||
|
|
||||||
doc, response = user.get(res=docs.DocumentDef.t,
|
doc, response = user.get(res=documents.DocumentDef.t,
|
||||||
item='erasures/',
|
item='erasures/',
|
||||||
query=[
|
query=[
|
||||||
('filter', {'id': [snapshot['device']['id']]}),
|
('filter', {'id': [snapshot['device']['id']]}),
|
||||||
|
@ -60,6 +67,159 @@ def test_erasure_certificate_private_query(user: UserClient):
|
||||||
assert 'application/pdf' == response.content_type
|
assert 'application/pdf' == response.content_type
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_erasure_certificate_wrong_id(client: Client):
|
def test_erasure_certificate_wrong_id(client: Client):
|
||||||
client.get(res=docs.DocumentDef.t, item='erasures/this-is-not-an-id',
|
client.get(res=documents.DocumentDef.t, item='erasures/this-is-not-an-id',
|
||||||
status=teal.marshmallow.ValidationError)
|
status=teal.marshmallow.ValidationError)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
def test_export_basic_snapshot(user: UserClient):
|
||||||
|
"""Test export device information in a csv file."""
|
||||||
|
snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
||||||
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
|
item='devices/',
|
||||||
|
accept='text/csv',
|
||||||
|
query=[('filter', {'type': ['Computer']})])
|
||||||
|
f = StringIO(csv_str)
|
||||||
|
obj_csv = csv.reader(f, f)
|
||||||
|
export_csv = list(obj_csv)
|
||||||
|
|
||||||
|
# Open fixture csv and transform to list
|
||||||
|
with Path(__file__).parent.joinpath('files').joinpath('basic.csv').open() as csv_file:
|
||||||
|
obj_csv = csv.reader(csv_file)
|
||||||
|
fixture_csv = list(obj_csv)
|
||||||
|
|
||||||
|
assert isinstance(datetime.strptime(export_csv[1][8], '%c'), datetime), \
|
||||||
|
'Register in field is not a datetime'
|
||||||
|
|
||||||
|
# Pop dates fields from csv lists to compare them
|
||||||
|
fixture_csv[1] = fixture_csv[1][:8] + fixture_csv[1][9:]
|
||||||
|
export_csv[1] = export_csv[1][:8] + export_csv[1][9:]
|
||||||
|
|
||||||
|
assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
|
||||||
|
assert fixture_csv[1] == export_csv[1], 'Computer information are not equal'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
def test_export_full_snapshot(user: UserClient):
|
||||||
|
"""Test a export device with all information and a lot of components."""
|
||||||
|
snapshot, _ = user.post(file('real-eee-1001pxd.snapshot.11'), res=Snapshot)
|
||||||
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
|
item='devices/',
|
||||||
|
accept='text/csv',
|
||||||
|
query=[('filter', {'type': ['Computer']})])
|
||||||
|
f = StringIO(csv_str)
|
||||||
|
obj_csv = csv.reader(f, f)
|
||||||
|
export_csv = list(obj_csv)
|
||||||
|
|
||||||
|
# Open fixture csv and transform to list
|
||||||
|
with Path(__file__).parent.joinpath('files').joinpath('real-eee-1001pxd.csv').open() \
|
||||||
|
as csv_file:
|
||||||
|
obj_csv = csv.reader(csv_file)
|
||||||
|
fixture_csv = list(obj_csv)
|
||||||
|
|
||||||
|
assert isinstance(datetime.strptime(export_csv[1][8], '%c'), datetime), \
|
||||||
|
'Register in field is not a datetime'
|
||||||
|
|
||||||
|
# Pop dates fields from csv lists to compare them
|
||||||
|
fixture_csv[1] = fixture_csv[1][:8] + fixture_csv[1][9:]
|
||||||
|
export_csv[1] = export_csv[1][:8] + export_csv[1][9:]
|
||||||
|
|
||||||
|
assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
|
||||||
|
assert fixture_csv[1] == export_csv[1], 'Computer information are not equal'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
def test_export_empty(user: UserClient):
|
||||||
|
"""Test to check works correctly exporting csv without any information,
|
||||||
|
export a placeholder device.
|
||||||
|
"""
|
||||||
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
|
accept='text/csv',
|
||||||
|
item='devices/')
|
||||||
|
f = StringIO(csv_str)
|
||||||
|
obj_csv = csv.reader(f, f)
|
||||||
|
export_csv = list(obj_csv)
|
||||||
|
|
||||||
|
assert len(export_csv) == 0, 'Csv is not empty'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
def test_export_computer_monitor(user: UserClient):
|
||||||
|
"""Test a export device type computer monitor."""
|
||||||
|
snapshot, _ = user.post(file('computer-monitor.snapshot'), res=Snapshot)
|
||||||
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
|
item='devices/',
|
||||||
|
accept='text/csv',
|
||||||
|
query=[('filter', {'type': ['ComputerMonitor']})])
|
||||||
|
f = StringIO(csv_str)
|
||||||
|
obj_csv = csv.reader(f, f)
|
||||||
|
export_csv = list(obj_csv)
|
||||||
|
# Open fixture csv and transform to list
|
||||||
|
with Path(__file__).parent.joinpath('files').joinpath('computer-monitor.csv').open() \
|
||||||
|
as csv_file:
|
||||||
|
obj_csv = csv.reader(csv_file)
|
||||||
|
fixture_csv = list(obj_csv)
|
||||||
|
|
||||||
|
# Pop dates fields from csv lists to compare them
|
||||||
|
fixture_csv[1] = fixture_csv[1][:8]
|
||||||
|
export_csv[1] = export_csv[1][:8]
|
||||||
|
|
||||||
|
assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
|
||||||
|
assert fixture_csv[1] == export_csv[1], 'Component information are not equal'
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_keyboard(user: UserClient):
|
||||||
|
"""Test a export device type keyboard."""
|
||||||
|
snapshot, _ = user.post(file('keyboard.snapshot'), res=Snapshot)
|
||||||
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
|
item='devices/',
|
||||||
|
accept='text/csv',
|
||||||
|
query=[('filter', {'type': ['Keyboard']})])
|
||||||
|
f = StringIO(csv_str)
|
||||||
|
obj_csv = csv.reader(f, f)
|
||||||
|
export_csv = list(obj_csv)
|
||||||
|
# Open fixture csv and transform to list
|
||||||
|
with Path(__file__).parent.joinpath('files').joinpath('keyboard.csv').open() as csv_file:
|
||||||
|
obj_csv = csv.reader(csv_file)
|
||||||
|
fixture_csv = list(obj_csv)
|
||||||
|
|
||||||
|
# Pop dates fields from csv lists to compare them
|
||||||
|
fixture_csv[1] = fixture_csv[1][:8]
|
||||||
|
export_csv[1] = export_csv[1][:8]
|
||||||
|
|
||||||
|
assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
|
||||||
|
assert fixture_csv[1] == export_csv[1], 'Component information are not equal'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
def test_export_multiple_different_devices(user: UserClient):
|
||||||
|
"""Test function 'Export' of multiple different device types (like
|
||||||
|
computers, keyboards, monitors, etc..)
|
||||||
|
"""
|
||||||
|
# Open fixture csv and transform to list
|
||||||
|
with Path(__file__).parent.joinpath('files').joinpath('multiples_devices.csv').open() \
|
||||||
|
as csv_file:
|
||||||
|
fixture_csv = list(csv.reader(csv_file))
|
||||||
|
for row in fixture_csv:
|
||||||
|
del row[8] # We remove the 'Registered in' column
|
||||||
|
|
||||||
|
# Post all devices snapshots
|
||||||
|
snapshot_pc, _ = user.post(file('real-eee-1001pxd.snapshot.11'), res=Snapshot)
|
||||||
|
snapshot_empty, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
||||||
|
snapshot_keyboard, _ = user.post(file('keyboard.snapshot'), res=Snapshot)
|
||||||
|
snapshot_monitor, _ = user.post(file('computer-monitor.snapshot'), res=Snapshot)
|
||||||
|
|
||||||
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
|
item='devices/',
|
||||||
|
query=[('filter', {'type': ['Computer', 'Keyboard', 'Monitor']})],
|
||||||
|
accept='text/csv')
|
||||||
|
f = StringIO(csv_str)
|
||||||
|
obj_csv = csv.reader(f, f)
|
||||||
|
export_csv = list(obj_csv)
|
||||||
|
|
||||||
|
for row in export_csv:
|
||||||
|
del row[8]
|
||||||
|
|
||||||
|
assert fixture_csv == export_csv
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
from ereuse_devicehub.devicehub import Devicehub
|
|
||||||
|
|
||||||
|
|
||||||
def test_dummy(_app: Devicehub):
|
|
||||||
"""Tests the dummy cli command."""
|
|
||||||
runner = _app.test_cli_runner()
|
|
||||||
runner.invoke('dummy', '--yes')
|
|
||||||
with _app.app_context():
|
|
||||||
_app.db.drop_all()
|
|
|
@ -61,6 +61,7 @@ def tdb2(config):
|
||||||
return Devicehub(inventory='tdb2', config=config, db=db)
|
return Devicehub(inventory='tdb2', config=config, db=db)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_inventory_create_delete_user(cli, tdb1, tdb2):
|
def test_inventory_create_delete_user(cli, tdb1, tdb2):
|
||||||
"""Tests creating two inventories with users, one user has
|
"""Tests creating two inventories with users, one user has
|
||||||
access to the first inventory and the other to both. Finally, deletes
|
access to the first inventory and the other to both. Finally, deletes
|
||||||
|
@ -136,6 +137,7 @@ def test_inventory_create_delete_user(cli, tdb1, tdb2):
|
||||||
assert db.has_schema('tdb2')
|
assert db.has_schema('tdb2')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_create_existing_inventory(cli, tdb1):
|
def test_create_existing_inventory(cli, tdb1):
|
||||||
"""Tries to create twice the same inventory."""
|
"""Tries to create twice the same inventory."""
|
||||||
cli.inv('tdb1')
|
cli.inv('tdb1')
|
||||||
|
|
|
@ -22,6 +22,7 @@ from tests import conftest
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_lot_model_children():
|
def test_lot_model_children():
|
||||||
"""Tests the property Lot.children
|
"""Tests the property Lot.children
|
||||||
|
@ -65,6 +66,7 @@ def test_lot_model_children():
|
||||||
assert not l3.parents
|
assert not l3.parents
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_lot_modify_patch_endpoint_and_delete(user: UserClient):
|
def test_lot_modify_patch_endpoint_and_delete(user: UserClient):
|
||||||
"""Creates and modifies lot properties through the endpoint."""
|
"""Creates and modifies lot properties through the endpoint."""
|
||||||
l, _ = user.post({'name': 'foo', 'description': 'baz'}, res=Lot)
|
l, _ = user.post({'name': 'foo', 'description': 'baz'}, res=Lot)
|
||||||
|
@ -79,6 +81,7 @@ def test_lot_modify_patch_endpoint_and_delete(user: UserClient):
|
||||||
user.get(res=Lot, item=l['id'], status=404)
|
user.get(res=Lot, item=l['id'], status=404)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_lot_device_relationship():
|
def test_lot_device_relationship():
|
||||||
device = Desktop(serial_number='foo',
|
device = Desktop(serial_number='foo',
|
||||||
|
@ -285,6 +288,7 @@ def test_lot_unite_graphs_and_find():
|
||||||
assert l4 not in l3 and l5 not in l3 and l6 not in l3 and l7 not in l3 and l8 not in l3
|
assert l4 not in l3 and l5 not in l3 and l6 not in l3 and l7 not in l3 and l8 not in l3
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_lot_roots():
|
def test_lot_roots():
|
||||||
"""Tests getting the method Lot.roots."""
|
"""Tests getting the method Lot.roots."""
|
||||||
|
@ -298,6 +302,7 @@ def test_lot_roots():
|
||||||
assert set(Lot.roots()) == {l1, l3}
|
assert set(Lot.roots()) == {l1, l3}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_post_get_lot(user: UserClient):
|
def test_post_get_lot(user: UserClient):
|
||||||
"""Tests submitting and retreiving a basic lot."""
|
"""Tests submitting and retreiving a basic lot."""
|
||||||
l, _ = user.post({'name': 'Foo'}, res=Lot)
|
l, _ = user.post({'name': 'Foo'}, res=Lot)
|
||||||
|
@ -345,6 +350,8 @@ def test_lot_post_add_children_view_ui_tree_normal(user: UserClient):
|
||||||
assert lots[0]['name'] == 'Parent'
|
assert lots[0]['name'] == 'Parent'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.xfail(reason='It needs to be fixed.')
|
||||||
def test_lot_post_add_remove_device_view(app: Devicehub, user: UserClient):
|
def test_lot_post_add_remove_device_view(app: Devicehub, user: UserClient):
|
||||||
"""Tests adding a device to a lot using POST and
|
"""Tests adding a device to a lot using POST and
|
||||||
removing it with DELETE.
|
removing it with DELETE.
|
||||||
|
|
|
@ -15,6 +15,7 @@ from tests import conftest
|
||||||
from tests.conftest import file
|
from tests.conftest import file
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_workbench_rate_db():
|
def test_workbench_rate_db():
|
||||||
rate = RateComputer(processor=0.1,
|
rate = RateComputer(processor=0.1,
|
||||||
|
@ -82,18 +83,19 @@ def test_price_from_rate():
|
||||||
device=pc)
|
device=pc)
|
||||||
_, price = RateComputer.compute(pc)
|
_, price = RateComputer.compute(pc)
|
||||||
|
|
||||||
assert price.price == Decimal('92.2001')
|
assert price.price == Decimal('78.2001')
|
||||||
assert price.retailer.standard.amount == Decimal('40.9714')
|
assert price.retailer.standard.amount == Decimal('34.7502')
|
||||||
assert price.platform.standard.amount == Decimal('18.8434')
|
assert price.platform.standard.amount == Decimal('15.9821')
|
||||||
assert price.refurbisher.standard.amount == Decimal('32.3853')
|
assert price.refurbisher.standard.amount == Decimal('27.4678')
|
||||||
assert price.price >= price.retailer.standard.amount + price.platform.standard.amount \
|
assert price.price >= price.retailer.standard.amount + price.platform.standard.amount \
|
||||||
+ price.refurbisher.standard.amount
|
+ price.refurbisher.standard.amount
|
||||||
assert price.retailer.warranty2.amount == Decimal('55.3085')
|
assert price.retailer.warranty2.amount == Decimal('46.9103')
|
||||||
assert price.platform.warranty2.amount == Decimal('25.4357')
|
assert price.platform.warranty2.amount == Decimal('21.5735')
|
||||||
assert price.refurbisher.warranty2.amount == Decimal('43.7259')
|
assert price.refurbisher.warranty2.amount == Decimal('37.0864')
|
||||||
assert price.warranty2 == Decimal('124.47')
|
assert price.warranty2 == Decimal('105.57')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_when_rate_must_not_compute(user: UserClient):
|
def test_when_rate_must_not_compute(user: UserClient):
|
||||||
"""Test to check if rate is computed in case of should not be calculated:
|
"""Test to check if rate is computed in case of should not be calculated:
|
||||||
1. Snapshot haven't visual test
|
1. Snapshot haven't visual test
|
||||||
|
@ -130,6 +132,7 @@ def test_when_rate_must_not_compute(user: UserClient):
|
||||||
assert 'rate' not in snapshot['device']
|
assert 'rate' not in snapshot['device']
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_multiple_rates(user: UserClient):
|
def test_multiple_rates(user: UserClient):
|
||||||
"""Tests submitting two rates from Workbench,
|
"""Tests submitting two rates from Workbench,
|
||||||
|
@ -164,12 +167,12 @@ def test_multiple_rates(user: UserClient):
|
||||||
assert rate1.processor == 3.95
|
assert rate1.processor == 3.95
|
||||||
assert rate1.ram == 3.8
|
assert rate1.ram == 3.8
|
||||||
|
|
||||||
assert rate1.appearance == 0.3
|
assert rate1.appearance is None
|
||||||
assert rate1.functionality == 0.4
|
assert rate1.functionality is None
|
||||||
|
|
||||||
assert rate1.rating == 4.62
|
assert rate1.rating == 3.92
|
||||||
|
|
||||||
assert price1.price == Decimal('92.4001')
|
assert price1.price == Decimal('78.4001')
|
||||||
|
|
||||||
cpu.actions_one.add(BenchmarkProcessor(rate=16069.44))
|
cpu.actions_one.add(BenchmarkProcessor(rate=16069.44))
|
||||||
ssd = SolidStateDrive(size=476940)
|
ssd = SolidStateDrive(size=476940)
|
||||||
|
@ -191,9 +194,9 @@ def test_multiple_rates(user: UserClient):
|
||||||
assert rate2.processor == 3.78
|
assert rate2.processor == 3.78
|
||||||
assert rate2.ram == 3.95
|
assert rate2.ram == 3.95
|
||||||
|
|
||||||
assert rate2.appearance == 0
|
assert rate2.appearance is None
|
||||||
assert rate2.functionality == -0.5
|
assert rate2.functionality is None
|
||||||
|
|
||||||
assert rate2.rating == 3.43
|
assert rate2.rating == 3.93
|
||||||
|
|
||||||
assert price2.price == Decimal('68.6001')
|
assert price2.price == Decimal('78.6001')
|
||||||
|
|
|
@ -28,6 +28,7 @@ from ereuse_devicehub.resources.enums import AppearanceRange, ComputerChassis, F
|
||||||
from tests import conftest
|
from tests import conftest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_rate_data_storage_rate():
|
def test_rate_data_storage_rate():
|
||||||
"""Test to check if compute data storage rate have same value than
|
"""Test to check if compute data storage rate have same value than
|
||||||
previous score version.
|
previous score version.
|
||||||
|
@ -63,6 +64,7 @@ def test_rate_data_storage_rate():
|
||||||
assert math.isclose(data_storage_rate, 3.70, rel_tol=0.001)
|
assert math.isclose(data_storage_rate, 3.70, rel_tol=0.001)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_rate_data_storage_size_is_null():
|
def test_rate_data_storage_size_is_null():
|
||||||
"""Test where input DataStorage.size = NULL, BenchmarkDataStorage.read_speed = 0,
|
"""Test where input DataStorage.size = NULL, BenchmarkDataStorage.read_speed = 0,
|
||||||
BenchmarkDataStorage.write_speed = 0 is like no DataStorage has been detected;
|
BenchmarkDataStorage.write_speed = 0 is like no DataStorage has been detected;
|
||||||
|
@ -75,6 +77,7 @@ def test_rate_data_storage_size_is_null():
|
||||||
assert data_storage_rate is None
|
assert data_storage_rate is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_rate_no_data_storage():
|
def test_rate_no_data_storage():
|
||||||
"""Test without data storage devices."""
|
"""Test without data storage devices."""
|
||||||
|
|
||||||
|
@ -84,6 +87,7 @@ def test_rate_no_data_storage():
|
||||||
assert data_storage_rate is None
|
assert data_storage_rate is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_rate_ram_rate():
|
def test_rate_ram_rate():
|
||||||
"""Test to check if compute ram rate have same value than previous
|
"""Test to check if compute ram rate have same value than previous
|
||||||
score version only with 1 RamModule.
|
score version only with 1 RamModule.
|
||||||
|
@ -96,6 +100,7 @@ def test_rate_ram_rate():
|
||||||
assert math.isclose(ram_rate, 2.02, rel_tol=0.002), 'RamRate returns incorrect value(rate)'
|
assert math.isclose(ram_rate, 2.02, rel_tol=0.002), 'RamRate returns incorrect value(rate)'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_rate_ram_rate_2modules():
|
def test_rate_ram_rate_2modules():
|
||||||
"""Test to check if compute ram rate have same value than previous
|
"""Test to check if compute ram rate have same value than previous
|
||||||
score version with 2 RamModule.
|
score version with 2 RamModule.
|
||||||
|
@ -109,6 +114,7 @@ def test_rate_ram_rate_2modules():
|
||||||
assert math.isclose(ram_rate, 3.79, rel_tol=0.001), 'RamRate returns incorrect value(rate)'
|
assert math.isclose(ram_rate, 3.79, rel_tol=0.001), 'RamRate returns incorrect value(rate)'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_rate_ram_rate_4modules():
|
def test_rate_ram_rate_4modules():
|
||||||
"""Test to check if compute ram rate have same value than previous
|
"""Test to check if compute ram rate have same value than previous
|
||||||
score version with 2 RamModule.
|
score version with 2 RamModule.
|
||||||
|
@ -124,6 +130,7 @@ def test_rate_ram_rate_4modules():
|
||||||
assert math.isclose(ram_rate, 1.993, rel_tol=0.001), 'RamRate returns incorrect value(rate)'
|
assert math.isclose(ram_rate, 1.993, rel_tol=0.001), 'RamRate returns incorrect value(rate)'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_rate_ram_module_size_is_0():
|
def test_rate_ram_module_size_is_0():
|
||||||
"""Test where input data RamModule.size = 0; is like no RamModule
|
"""Test where input data RamModule.size = 0; is like no RamModule
|
||||||
has been detected.
|
has been detected.
|
||||||
|
@ -135,6 +142,7 @@ def test_rate_ram_module_size_is_0():
|
||||||
assert ram_rate is None
|
assert ram_rate is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_rate_ram_speed_is_null():
|
def test_rate_ram_speed_is_null():
|
||||||
"""Test where RamModule.speed is NULL (not detected) but has size."""
|
"""Test where RamModule.speed is NULL (not detected) but has size."""
|
||||||
|
|
||||||
|
@ -151,6 +159,7 @@ def test_rate_ram_speed_is_null():
|
||||||
assert math.isclose(ram_rate, 1.25, rel_tol=0.004), 'RamRate returns incorrect value(rate)'
|
assert math.isclose(ram_rate, 1.25, rel_tol=0.004), 'RamRate returns incorrect value(rate)'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_rate_no_ram_module():
|
def test_rate_no_ram_module():
|
||||||
"""Test without RamModule."""
|
"""Test without RamModule."""
|
||||||
ram0 = RamModule()
|
ram0 = RamModule()
|
||||||
|
@ -159,6 +168,7 @@ def test_rate_no_ram_module():
|
||||||
assert ram_rate is None
|
assert ram_rate is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_rate_processor_rate():
|
def test_rate_processor_rate():
|
||||||
"""Test to check if compute processor rate have same value than previous
|
"""Test to check if compute processor rate have same value than previous
|
||||||
score version only with 1 core.
|
score version only with 1 core.
|
||||||
|
@ -173,6 +183,7 @@ def test_rate_processor_rate():
|
||||||
assert math.isclose(processor_rate, 1, rel_tol=0.001)
|
assert math.isclose(processor_rate, 1, rel_tol=0.001)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_rate_processor_rate_2cores():
|
def test_rate_processor_rate_2cores():
|
||||||
"""Test to check if compute processor rate have same value than previous
|
"""Test to check if compute processor rate have same value than previous
|
||||||
score version with 2 cores.
|
score version with 2 cores.
|
||||||
|
@ -194,6 +205,7 @@ def test_rate_processor_rate_2cores():
|
||||||
assert math.isclose(processor_rate, 3.93, rel_tol=0.002)
|
assert math.isclose(processor_rate, 3.93, rel_tol=0.002)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_rate_processor_with_null_cores():
|
def test_rate_processor_with_null_cores():
|
||||||
"""Test with processor device have null number of cores."""
|
"""Test with processor device have null number of cores."""
|
||||||
cpu = Processor(cores=None, speed=3.3)
|
cpu = Processor(cores=None, speed=3.3)
|
||||||
|
@ -204,6 +216,7 @@ def test_rate_processor_with_null_cores():
|
||||||
assert math.isclose(processor_rate, 1.38, rel_tol=0.003)
|
assert math.isclose(processor_rate, 1.38, rel_tol=0.003)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_rate_processor_with_null_speed():
|
def test_rate_processor_with_null_speed():
|
||||||
"""Test with processor device have null speed value."""
|
"""Test with processor device have null speed value."""
|
||||||
cpu = Processor(cores=1, speed=None)
|
cpu = Processor(cores=1, speed=None)
|
||||||
|
@ -214,6 +227,7 @@ def test_rate_processor_with_null_speed():
|
||||||
assert math.isclose(processor_rate, 1.06, rel_tol=0.001)
|
assert math.isclose(processor_rate, 1.06, rel_tol=0.001)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_rate_computer_1193():
|
def test_rate_computer_1193():
|
||||||
"""Test rate computer characteristics:
|
"""Test rate computer characteristics:
|
||||||
|
@ -264,9 +278,10 @@ def test_rate_computer_1193():
|
||||||
|
|
||||||
assert math.isclose(rate_pc.processor, 3.95, rel_tol=0.001)
|
assert math.isclose(rate_pc.processor, 3.95, rel_tol=0.001)
|
||||||
|
|
||||||
assert math.isclose(rate_pc.rating, 4.61, rel_tol=0.001)
|
assert math.isclose(rate_pc.rating, 3.91, rel_tol=0.001)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_rate_computer_1201():
|
def test_rate_computer_1201():
|
||||||
"""Test rate computer characteristics:
|
"""Test rate computer characteristics:
|
||||||
|
@ -315,9 +330,10 @@ def test_rate_computer_1201():
|
||||||
|
|
||||||
assert math.isclose(rate_pc.processor, 3.93, rel_tol=0.001)
|
assert math.isclose(rate_pc.processor, 3.93, rel_tol=0.001)
|
||||||
|
|
||||||
assert math.isclose(rate_pc.rating, 3.48, rel_tol=0.001)
|
assert math.isclose(rate_pc.rating, 3.08, rel_tol=0.001)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_rate_computer_multiple_ram_module():
|
def test_rate_computer_multiple_ram_module():
|
||||||
"""Test rate computer characteristics:
|
"""Test rate computer characteristics:
|
||||||
|
@ -373,9 +389,10 @@ def test_rate_computer_multiple_ram_module():
|
||||||
|
|
||||||
assert math.isclose(rate_pc.processor, 1, rel_tol=0.001)
|
assert math.isclose(rate_pc.processor, 1, rel_tol=0.001)
|
||||||
|
|
||||||
assert rate_pc.rating == 1.57
|
assert rate_pc.rating == 1.37
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_rate_computer_one_ram_module():
|
def test_rate_computer_one_ram_module():
|
||||||
"""Test rate computer characteristics:
|
"""Test rate computer characteristics:
|
||||||
|
@ -426,7 +443,7 @@ def test_rate_computer_one_ram_module():
|
||||||
|
|
||||||
assert math.isclose(rate_pc.processor, 4.09, rel_tol=0.001)
|
assert math.isclose(rate_pc.processor, 4.09, rel_tol=0.001)
|
||||||
|
|
||||||
assert math.isclose(rate_pc.rating, 2.5, rel_tol=0.001)
|
assert math.isclose(rate_pc.rating, 2.1, rel_tol=0.001)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reason='Data Storage rate actually requires a DSSBenchmark')
|
@pytest.mark.xfail(reason='Data Storage rate actually requires a DSSBenchmark')
|
||||||
|
|
|
@ -1,156 +0,0 @@
|
||||||
import csv
|
|
||||||
from datetime import datetime
|
|
||||||
from io import StringIO
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from ereuse_devicehub.client import UserClient
|
|
||||||
from ereuse_devicehub.resources.action.models import Snapshot
|
|
||||||
from ereuse_devicehub.resources.documents import documents
|
|
||||||
from tests.conftest import file
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_basic_snapshot(user: UserClient):
|
|
||||||
"""Test export device information in a csv file."""
|
|
||||||
snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
|
||||||
item='devices/',
|
|
||||||
accept='text/csv',
|
|
||||||
query=[('filter', {'type': ['Computer']})])
|
|
||||||
f = StringIO(csv_str)
|
|
||||||
obj_csv = csv.reader(f, f)
|
|
||||||
export_csv = list(obj_csv)
|
|
||||||
|
|
||||||
# Open fixture csv and transform to list
|
|
||||||
with Path(__file__).parent.joinpath('files').joinpath('basic.csv').open() as csv_file:
|
|
||||||
obj_csv = csv.reader(csv_file)
|
|
||||||
fixture_csv = list(obj_csv)
|
|
||||||
|
|
||||||
assert isinstance(datetime.strptime(export_csv[1][8], '%c'), datetime), \
|
|
||||||
'Register in field is not a datetime'
|
|
||||||
|
|
||||||
# Pop dates fields from csv lists to compare them
|
|
||||||
fixture_csv[1] = fixture_csv[1][:8] + fixture_csv[1][9:]
|
|
||||||
export_csv[1] = export_csv[1][:8] + export_csv[1][9:]
|
|
||||||
|
|
||||||
assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
|
|
||||||
assert fixture_csv[1] == export_csv[1], 'Computer information are not equal'
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_full_snapshot(user: UserClient):
|
|
||||||
"""Test a export device with all information and a lot of components."""
|
|
||||||
snapshot, _ = user.post(file('real-eee-1001pxd.snapshot.11'), res=Snapshot)
|
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
|
||||||
item='devices/',
|
|
||||||
accept='text/csv',
|
|
||||||
query=[('filter', {'type': ['Computer']})])
|
|
||||||
f = StringIO(csv_str)
|
|
||||||
obj_csv = csv.reader(f, f)
|
|
||||||
export_csv = list(obj_csv)
|
|
||||||
|
|
||||||
# Open fixture csv and transform to list
|
|
||||||
with Path(__file__).parent.joinpath('files').joinpath('real-eee-1001pxd.csv').open() \
|
|
||||||
as csv_file:
|
|
||||||
obj_csv = csv.reader(csv_file)
|
|
||||||
fixture_csv = list(obj_csv)
|
|
||||||
|
|
||||||
assert isinstance(datetime.strptime(export_csv[1][8], '%c'), datetime), \
|
|
||||||
'Register in field is not a datetime'
|
|
||||||
|
|
||||||
# Pop dates fields from csv lists to compare them
|
|
||||||
fixture_csv[1] = fixture_csv[1][:8] + fixture_csv[1][9:]
|
|
||||||
export_csv[1] = export_csv[1][:8] + export_csv[1][9:]
|
|
||||||
|
|
||||||
assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
|
|
||||||
assert fixture_csv[1] == export_csv[1], 'Computer information are not equal'
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_empty(user: UserClient):
|
|
||||||
"""Test to check works correctly exporting csv without any information,
|
|
||||||
export a placeholder device.
|
|
||||||
"""
|
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
|
||||||
accept='text/csv',
|
|
||||||
item='devices/')
|
|
||||||
f = StringIO(csv_str)
|
|
||||||
obj_csv = csv.reader(f, f)
|
|
||||||
export_csv = list(obj_csv)
|
|
||||||
|
|
||||||
assert len(export_csv) == 0, 'Csv is not empty'
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_computer_monitor(user: UserClient):
|
|
||||||
"""Test a export device type computer monitor."""
|
|
||||||
snapshot, _ = user.post(file('computer-monitor.snapshot'), res=Snapshot)
|
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
|
||||||
item='devices/',
|
|
||||||
accept='text/csv',
|
|
||||||
query=[('filter', {'type': ['ComputerMonitor']})])
|
|
||||||
f = StringIO(csv_str)
|
|
||||||
obj_csv = csv.reader(f, f)
|
|
||||||
export_csv = list(obj_csv)
|
|
||||||
# Open fixture csv and transform to list
|
|
||||||
with Path(__file__).parent.joinpath('files').joinpath('computer-monitor.csv').open() \
|
|
||||||
as csv_file:
|
|
||||||
obj_csv = csv.reader(csv_file)
|
|
||||||
fixture_csv = list(obj_csv)
|
|
||||||
|
|
||||||
# Pop dates fields from csv lists to compare them
|
|
||||||
fixture_csv[1] = fixture_csv[1][:8]
|
|
||||||
export_csv[1] = export_csv[1][:8]
|
|
||||||
|
|
||||||
assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
|
|
||||||
assert fixture_csv[1] == export_csv[1], 'Component information are not equal'
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_keyboard(user: UserClient):
|
|
||||||
"""Test a export device type keyboard."""
|
|
||||||
snapshot, _ = user.post(file('keyboard.snapshot'), res=Snapshot)
|
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
|
||||||
item='devices/',
|
|
||||||
accept='text/csv',
|
|
||||||
query=[('filter', {'type': ['Keyboard']})])
|
|
||||||
f = StringIO(csv_str)
|
|
||||||
obj_csv = csv.reader(f, f)
|
|
||||||
export_csv = list(obj_csv)
|
|
||||||
# Open fixture csv and transform to list
|
|
||||||
with Path(__file__).parent.joinpath('files').joinpath('keyboard.csv').open() as csv_file:
|
|
||||||
obj_csv = csv.reader(csv_file)
|
|
||||||
fixture_csv = list(obj_csv)
|
|
||||||
|
|
||||||
# Pop dates fields from csv lists to compare them
|
|
||||||
fixture_csv[1] = fixture_csv[1][:8]
|
|
||||||
export_csv[1] = export_csv[1][:8]
|
|
||||||
|
|
||||||
assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
|
|
||||||
assert fixture_csv[1] == export_csv[1], 'Component information are not equal'
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_multiple_different_devices(user: UserClient):
|
|
||||||
"""Test function 'Export' of multiple different device types (like
|
|
||||||
computers, keyboards, monitors, etc..)
|
|
||||||
"""
|
|
||||||
# Open fixture csv and transform to list
|
|
||||||
with Path(__file__).parent.joinpath('files').joinpath('multiples_devices.csv').open() \
|
|
||||||
as csv_file:
|
|
||||||
fixture_csv = list(csv.reader(csv_file))
|
|
||||||
for row in fixture_csv:
|
|
||||||
del row[8] # We remove the 'Registered in' column
|
|
||||||
|
|
||||||
# Post all devices snapshots
|
|
||||||
snapshot_pc, _ = user.post(file('real-eee-1001pxd.snapshot.11'), res=Snapshot)
|
|
||||||
snapshot_empty, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
|
||||||
snapshot_keyboard, _ = user.post(file('keyboard.snapshot'), res=Snapshot)
|
|
||||||
snapshot_monitor, _ = user.post(file('computer-monitor.snapshot'), res=Snapshot)
|
|
||||||
|
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
|
||||||
item='devices/',
|
|
||||||
query=[('filter', {'type': ['Computer', 'Keyboard', 'Monitor']})],
|
|
||||||
accept='text/csv')
|
|
||||||
f = StringIO(csv_str)
|
|
||||||
obj_csv = csv.reader(f, f)
|
|
||||||
export_csv = list(obj_csv)
|
|
||||||
|
|
||||||
for row in export_csv:
|
|
||||||
del row[8]
|
|
||||||
|
|
||||||
assert fixture_csv == export_csv
|
|
|
@ -12,8 +12,7 @@ from ereuse_devicehub.client import UserClient
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.devicehub import Devicehub
|
from ereuse_devicehub.devicehub import Devicehub
|
||||||
from ereuse_devicehub.resources.action.models import Action, BenchmarkDataStorage, \
|
from ereuse_devicehub.resources.action.models import Action, BenchmarkDataStorage, \
|
||||||
BenchmarkProcessor, EraseSectors, RateComputer, Snapshot, SnapshotRequest, VisualTest, \
|
BenchmarkProcessor, EraseSectors, RateComputer, Snapshot, SnapshotRequest, VisualTest
|
||||||
MeasureBattery, BenchmarkRamSysbench, StressTest
|
|
||||||
from ereuse_devicehub.resources.device import models as m
|
from ereuse_devicehub.resources.device import models as m
|
||||||
from ereuse_devicehub.resources.device.exceptions import NeedsId
|
from ereuse_devicehub.resources.device.exceptions import NeedsId
|
||||||
from ereuse_devicehub.resources.device.models import SolidStateDrive
|
from ereuse_devicehub.resources.device.models import SolidStateDrive
|
||||||
|
@ -25,6 +24,7 @@ from ereuse_devicehub.resources.user.models import User
|
||||||
from tests.conftest import file
|
from tests.conftest import file
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures('auth_app_context')
|
@pytest.mark.usefixtures('auth_app_context')
|
||||||
def test_snapshot_model():
|
def test_snapshot_model():
|
||||||
"""Tests creating a Snapshot with its relationships ensuring correct
|
"""Tests creating a Snapshot with its relationships ensuring correct
|
||||||
|
@ -55,12 +55,14 @@ def test_snapshot_model():
|
||||||
assert device.url == urlutils.URL('http://localhost/devices/1')
|
assert device.url == urlutils.URL('http://localhost/devices/1')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_snapshot_schema(app: Devicehub):
|
def test_snapshot_schema(app: Devicehub):
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
s = file('basic.snapshot')
|
s = file('basic.snapshot')
|
||||||
app.resources['Snapshot'].schema.load(s)
|
app.resources['Snapshot'].schema.load(s)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_snapshot_post(user: UserClient):
|
def test_snapshot_post(user: UserClient):
|
||||||
"""Tests the post snapshot endpoint (validation, etc), data correctness,
|
"""Tests the post snapshot endpoint (validation, etc), data correctness,
|
||||||
and relationship correctness.
|
and relationship correctness.
|
||||||
|
@ -96,6 +98,8 @@ def test_snapshot_post(user: UserClient):
|
||||||
assert rate['snapshot']['id'] == snapshot['id']
|
assert rate['snapshot']['id'] == snapshot['id']
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.xfail(reason='Needs to fix it')
|
||||||
def test_snapshot_component_add_remove(user: UserClient):
|
def test_snapshot_component_add_remove(user: UserClient):
|
||||||
"""Tests adding and removing components and some don't generate HID.
|
"""Tests adding and removing components and some don't generate HID.
|
||||||
All computers generate HID.
|
All computers generate HID.
|
||||||
|
@ -229,6 +233,8 @@ def _test_snapshot_computer_no_hid(user: UserClient):
|
||||||
user.post(s, res=Snapshot)
|
user.post(s, res=Snapshot)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.xfail(reason='Needs to fix it')
|
||||||
def test_snapshot_post_without_hid(user: UserClient):
|
def test_snapshot_post_without_hid(user: UserClient):
|
||||||
"""Tests the post snapshot endpoint (validation, etc), data correctness,
|
"""Tests the post snapshot endpoint (validation, etc), data correctness,
|
||||||
and relationship correctness with HID field generated with type - model - manufacturer - S/N.
|
and relationship correctness with HID field generated with type - model - manufacturer - S/N.
|
||||||
|
@ -247,10 +253,12 @@ def test_snapshot_post_without_hid(user: UserClient):
|
||||||
assert snapshot['author']['id'] == user.user['id']
|
assert snapshot['author']['id'] == user.user['id']
|
||||||
assert 'actions' not in snapshot['device']
|
assert 'actions' not in snapshot['device']
|
||||||
assert 'author' not in snapshot['device']
|
assert 'author' not in snapshot['device']
|
||||||
|
assert snapshot['severity'] == 'Warning'
|
||||||
response = user.post(snapshot, res=Snapshot)
|
response = user.post(snapshot, res=Snapshot)
|
||||||
assert response.status == 201
|
assert response.status == 201
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_snapshot_mismatch_id():
|
def test_snapshot_mismatch_id():
|
||||||
"""Tests uploading a device with an ID from another device."""
|
"""Tests uploading a device with an ID from another device."""
|
||||||
# Note that this won't happen as in this new version
|
# Note that this won't happen as in this new version
|
||||||
|
@ -258,6 +266,7 @@ def test_snapshot_mismatch_id():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_snapshot_tag_inner_tag(tag_id: str, user: UserClient, app: Devicehub):
|
def test_snapshot_tag_inner_tag(tag_id: str, user: UserClient, app: Devicehub):
|
||||||
"""Tests a posting Snapshot with a local tag."""
|
"""Tests a posting Snapshot with a local tag."""
|
||||||
b = file('basic.snapshot')
|
b = file('basic.snapshot')
|
||||||
|
@ -270,6 +279,7 @@ def test_snapshot_tag_inner_tag(tag_id: str, user: UserClient, app: Devicehub):
|
||||||
assert tag.device_id == 1, 'Tag should be linked to the first device'
|
assert tag.device_id == 1, 'Tag should be linked to the first device'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_snapshot_tag_inner_tag_mismatch_between_tags_and_hid(user: UserClient, tag_id: str):
|
def test_snapshot_tag_inner_tag_mismatch_between_tags_and_hid(user: UserClient, tag_id: str):
|
||||||
"""Ensures one device cannot 'steal' the tag from another one."""
|
"""Ensures one device cannot 'steal' the tag from another one."""
|
||||||
pc1 = file('basic.snapshot')
|
pc1 = file('basic.snapshot')
|
||||||
|
@ -281,6 +291,7 @@ def test_snapshot_tag_inner_tag_mismatch_between_tags_and_hid(user: UserClient,
|
||||||
user.post(pc2, res=Snapshot, status=MismatchBetweenTagsAndHid)
|
user.post(pc2, res=Snapshot, status=MismatchBetweenTagsAndHid)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_snapshot_different_properties_same_tags(user: UserClient, tag_id: str):
|
def test_snapshot_different_properties_same_tags(user: UserClient, tag_id: str):
|
||||||
"""Tests a snapshot performed to device 1 with tag A and then to
|
"""Tests a snapshot performed to device 1 with tag A and then to
|
||||||
device 2 with tag B. Both don't have HID but are different type.
|
device 2 with tag B. Both don't have HID but are different type.
|
||||||
|
@ -300,12 +311,14 @@ def test_snapshot_different_properties_same_tags(user: UserClient, tag_id: str):
|
||||||
user.post(pc2, res=Snapshot, status=MismatchBetweenProperties)
|
user.post(pc2, res=Snapshot, status=MismatchBetweenProperties)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_snapshot_upload_twice_uuid_error(user: UserClient):
|
def test_snapshot_upload_twice_uuid_error(user: UserClient):
|
||||||
pc1 = file('basic.snapshot')
|
pc1 = file('basic.snapshot')
|
||||||
user.post(pc1, res=Snapshot)
|
user.post(pc1, res=Snapshot)
|
||||||
user.post(pc1, res=Snapshot, status=UniqueViolation)
|
user.post(pc1, res=Snapshot, status=UniqueViolation)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_snapshot_component_containing_components(user: UserClient):
|
def test_snapshot_component_containing_components(user: UserClient):
|
||||||
"""There is no reason for components to have components and when
|
"""There is no reason for components to have components and when
|
||||||
this happens it is always an error.
|
this happens it is always an error.
|
||||||
|
@ -322,6 +335,8 @@ def test_snapshot_component_containing_components(user: UserClient):
|
||||||
user.post(s, res=Snapshot, status=ValidationError)
|
user.post(s, res=Snapshot, status=ValidationError)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.xfail(reason='It needs to be fixed.')
|
||||||
def test_erase_privacy_standards_endtime_sort(user: UserClient):
|
def test_erase_privacy_standards_endtime_sort(user: UserClient):
|
||||||
"""Tests a Snapshot with EraseSectors and the resulting privacy
|
"""Tests a Snapshot with EraseSectors and the resulting privacy
|
||||||
properties.
|
properties.
|
||||||
|
@ -401,37 +416,6 @@ def test_test_data_storage(user: UserClient):
|
||||||
assert incidence_test['severity'] == 'Error'
|
assert incidence_test['severity'] == 'Error'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reason='Not implemented yet, new rate is need it')
|
|
||||||
def test_snapshot_computer_monitor(user: UserClient):
|
|
||||||
"""Tests that a snapshot of computer monitor device create correctly."""
|
|
||||||
s = file('computer-monitor.snapshot')
|
|
||||||
snapshot_and_check(user, s, action_types=('RateMonitor',))
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reason='Not implemented yet, new rate is need it')
|
|
||||||
def test_snapshot_mobile_smartphone_imei_manual_rate(user: UserClient):
|
|
||||||
"""Tests that a snapshot of smartphone device is creat correctly."""
|
|
||||||
s = file('smartphone.snapshot')
|
|
||||||
snapshot = snapshot_and_check(user, s, action_types=('VisualTest',))
|
|
||||||
mobile, _ = user.get(res=m.Device, item=snapshot['device']['id'])
|
|
||||||
assert mobile['imei'] == 3568680000414120
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reason='Test not developed')
|
|
||||||
def test_snapshot_components_none():
|
|
||||||
"""Tests that a snapshot without components does not remove them
|
|
||||||
from the computer.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
# TODO JN is really necessary in which cases??
|
|
||||||
@pytest.mark.xfail(reason='Test not developed')
|
|
||||||
def test_snapshot_components_empty():
|
|
||||||
"""Tests that a snapshot whose components are an empty list remove
|
|
||||||
all its components.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def assert_similar_device(device1: dict, device2: dict):
|
def assert_similar_device(device1: dict, device2: dict):
|
||||||
"""Like :class:`ereuse_devicehub.resources.device.models.Device.
|
"""Like :class:`ereuse_devicehub.resources.device.models.Device.
|
||||||
is_similar()` but adapted for testing.
|
is_similar()` but adapted for testing.
|
||||||
|
@ -497,14 +481,7 @@ def snapshot_and_check(user: UserClient,
|
||||||
return snapshot
|
return snapshot
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reason='Not implemented yet, new rate is need it')
|
@pytest.mark.mvp
|
||||||
def test_snapshot_keyboard(user: UserClient):
|
|
||||||
s = file('keyboard.snapshot')
|
|
||||||
snapshot = snapshot_and_check(user, s, action_types=('VisualTest',))
|
|
||||||
keyboard = snapshot['device']
|
|
||||||
assert keyboard['layout'] == 'ES'
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reason='Debug and rewrite it')
|
@pytest.mark.xfail(reason='Debug and rewrite it')
|
||||||
def test_pc_rating_rate_none(user: UserClient):
|
def test_pc_rating_rate_none(user: UserClient):
|
||||||
"""Tests a Snapshot with EraseSectors."""
|
"""Tests a Snapshot with EraseSectors."""
|
||||||
|
@ -513,14 +490,7 @@ def test_pc_rating_rate_none(user: UserClient):
|
||||||
snapshot, _ = user.post(res=Snapshot, data=s)
|
snapshot, _ = user.post(res=Snapshot, data=s)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_pc_2(user: UserClient):
|
def test_pc_2(user: UserClient):
|
||||||
s = file('laptop-hp_255_g3_notebook-hewlett-packard-cnd52270fw.snapshot')
|
s = file('laptop-hp_255_g3_notebook-hewlett-packard-cnd52270fw.snapshot')
|
||||||
snapshot, _ = user.post(res=Snapshot, data=s)
|
snapshot, _ = user.post(res=Snapshot, data=s)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reason='Add battery component assets')
|
|
||||||
def test_snapshot_pc_with_battery_component(user: UserClient):
|
|
||||||
pc1 = file('acer.happy.battery.snapshot')
|
|
||||||
snapshot = snapshot_and_check(user, pc1,
|
|
||||||
action_types=(StressTest.t, BenchmarkRamSysbench.t),
|
|
||||||
perform_second_snapshot=False)
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ from tests import conftest
|
||||||
from tests.conftest import file
|
from tests.conftest import file
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_create_tag():
|
def test_create_tag():
|
||||||
"""Creates a tag specifying a custom organization."""
|
"""Creates a tag specifying a custom organization."""
|
||||||
|
@ -34,6 +35,7 @@ def test_create_tag():
|
||||||
assert tag.provider == URL('http://foo.bar')
|
assert tag.provider == URL('http://foo.bar')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_create_tag_default_org():
|
def test_create_tag_default_org():
|
||||||
"""Creates a tag using the default organization."""
|
"""Creates a tag using the default organization."""
|
||||||
|
@ -47,6 +49,7 @@ def test_create_tag_default_org():
|
||||||
assert tag.org.name == 'FooOrg' # as defined in the settings
|
assert tag.org.name == 'FooOrg' # as defined in the settings
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_create_tag_no_slash():
|
def test_create_tag_no_slash():
|
||||||
"""Checks that no tags can be created that contain a slash."""
|
"""Checks that no tags can be created that contain a slash."""
|
||||||
|
@ -57,6 +60,7 @@ def test_create_tag_no_slash():
|
||||||
Tag('bar', secondary='/')
|
Tag('bar', secondary='/')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_create_two_same_tags():
|
def test_create_two_same_tags():
|
||||||
"""Ensures there cannot be two tags with the same ID and organization."""
|
"""Ensures there cannot be two tags with the same ID and organization."""
|
||||||
|
@ -72,6 +76,7 @@ def test_create_two_same_tags():
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_tag_post(app: Devicehub, user: UserClient):
|
def test_tag_post(app: Devicehub, user: UserClient):
|
||||||
"""Checks the POST method of creating a tag."""
|
"""Checks the POST method of creating a tag."""
|
||||||
user.post({'id': 'foo'}, res=Tag)
|
user.post({'id': 'foo'}, res=Tag)
|
||||||
|
@ -79,6 +84,7 @@ def test_tag_post(app: Devicehub, user: UserClient):
|
||||||
assert Tag.query.filter_by(id='foo').one()
|
assert Tag.query.filter_by(id='foo').one()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_tag_post_etag(user: UserClient):
|
def test_tag_post_etag(user: UserClient):
|
||||||
"""Ensures users cannot create eReuse.org tags through POST;
|
"""Ensures users cannot create eReuse.org tags through POST;
|
||||||
only terminal.
|
only terminal.
|
||||||
|
@ -93,12 +99,13 @@ def test_tag_post_etag(user: UserClient):
|
||||||
user.post({'id': 'FOO-123456'}, res=Tag)
|
user.post({'id': 'FOO-123456'}, res=Tag)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_tag_get_device_from_tag_endpoint(app: Devicehub, user: UserClient):
|
def test_tag_get_device_from_tag_endpoint(app: Devicehub, user: UserClient):
|
||||||
"""Checks getting a linked device from a tag endpoint"""
|
"""Checks getting a linked device from a tag endpoint"""
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
# Create a pc with a tag
|
# Create a pc with a tag
|
||||||
tag = Tag(id='foo-bar')
|
tag = Tag(id='foo-bar')
|
||||||
pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower)
|
pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id'])
|
||||||
pc.tags.add(tag)
|
pc.tags.add(tag)
|
||||||
db.session.add(pc)
|
db.session.add(pc)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -106,6 +113,7 @@ def test_tag_get_device_from_tag_endpoint(app: Devicehub, user: UserClient):
|
||||||
assert computer['serialNumber'] == 'sn1'
|
assert computer['serialNumber'] == 'sn1'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_tag_get_device_from_tag_endpoint_no_linked(app: Devicehub, user: UserClient):
|
def test_tag_get_device_from_tag_endpoint_no_linked(app: Devicehub, user: UserClient):
|
||||||
"""As above, but when the tag is not linked."""
|
"""As above, but when the tag is not linked."""
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
|
@ -114,11 +122,13 @@ def test_tag_get_device_from_tag_endpoint_no_linked(app: Devicehub, user: UserCl
|
||||||
user.get(res=Tag, item='foo-bar/device', status=TagNotLinked)
|
user.get(res=Tag, item='foo-bar/device', status=TagNotLinked)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_tag_get_device_from_tag_endpoint_no_tag(user: UserClient):
|
def test_tag_get_device_from_tag_endpoint_no_tag(user: UserClient):
|
||||||
"""As above, but when there is no tag with such ID."""
|
"""As above, but when there is no tag with such ID."""
|
||||||
user.get(res=Tag, item='foo-bar/device', status=ResourceNotFound)
|
user.get(res=Tag, item='foo-bar/device', status=ResourceNotFound)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_tag_get_device_from_tag_endpoint_multiple_tags(app: Devicehub, user: UserClient):
|
def test_tag_get_device_from_tag_endpoint_multiple_tags(app: Devicehub, user: UserClient):
|
||||||
"""As above, but when there are two tags with the same ID, the
|
"""As above, but when there are two tags with the same ID, the
|
||||||
system should not return any of both (to be deterministic) so
|
system should not return any of both (to be deterministic) so
|
||||||
|
@ -132,6 +142,7 @@ def test_tag_get_device_from_tag_endpoint_multiple_tags(app: Devicehub, user: Us
|
||||||
user.get(res=Tag, item='foo-bar/device', status=MultipleResourcesFound)
|
user.get(res=Tag, item='foo-bar/device', status=MultipleResourcesFound)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_tag_create_tags_cli(app: Devicehub, user: UserClient):
|
def test_tag_create_tags_cli(app: Devicehub, user: UserClient):
|
||||||
"""Checks creating tags with the CLI endpoint."""
|
"""Checks creating tags with the CLI endpoint."""
|
||||||
runner = app.test_cli_runner()
|
runner = app.test_cli_runner()
|
||||||
|
@ -142,6 +153,7 @@ def test_tag_create_tags_cli(app: Devicehub, user: UserClient):
|
||||||
assert tag.org.id == Organization.get_default_org_id()
|
assert tag.org.id == Organization.get_default_org_id()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_tag_create_etags_cli(app: Devicehub, user: UserClient):
|
def test_tag_create_etags_cli(app: Devicehub, user: UserClient):
|
||||||
"""Creates an eTag through the CLI."""
|
"""Creates an eTag through the CLI."""
|
||||||
# todo what happens to organization?
|
# todo what happens to organization?
|
||||||
|
@ -154,6 +166,7 @@ def test_tag_create_etags_cli(app: Devicehub, user: UserClient):
|
||||||
assert tag.provider == URL('https://t.ereuse.org')
|
assert tag.provider == URL('https://t.ereuse.org')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_tag_manual_link_search(app: Devicehub, user: UserClient):
|
def test_tag_manual_link_search(app: Devicehub, user: UserClient):
|
||||||
"""Tests linking manually a tag through PUT /tags/<id>/device/<id>
|
"""Tests linking manually a tag through PUT /tags/<id>/device/<id>
|
||||||
|
|
||||||
|
@ -161,7 +174,7 @@ def test_tag_manual_link_search(app: Devicehub, user: UserClient):
|
||||||
"""
|
"""
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
db.session.add(Tag('foo-bar', secondary='foo-sec'))
|
db.session.add(Tag('foo-bar', secondary='foo-sec'))
|
||||||
desktop = Desktop(serial_number='foo', chassis=ComputerChassis.AllInOne)
|
desktop = Desktop(serial_number='foo', chassis=ComputerChassis.AllInOne, owner_id=user.user['id'])
|
||||||
db.session.add(desktop)
|
db.session.add(desktop)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
desktop_id = desktop.id
|
desktop_id = desktop.id
|
||||||
|
@ -189,6 +202,7 @@ def test_tag_manual_link_search(app: Devicehub, user: UserClient):
|
||||||
assert i['items']
|
assert i['items']
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_tag_secondary_workbench_link_find(user: UserClient):
|
def test_tag_secondary_workbench_link_find(user: UserClient):
|
||||||
"""Creates and consumes tags with a secondary id, linking them
|
"""Creates and consumes tags with a secondary id, linking them
|
||||||
|
@ -215,6 +229,7 @@ def test_tag_secondary_workbench_link_find(user: UserClient):
|
||||||
assert len(r['items']) == 1
|
assert len(r['items']) == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_tag_create_tags_cli_csv(app: Devicehub, user: UserClient):
|
def test_tag_create_tags_cli_csv(app: Devicehub, user: UserClient):
|
||||||
"""Checks creating tags with the CLI endpoint using a CSV."""
|
"""Checks creating tags with the CLI endpoint using a CSV."""
|
||||||
csv = pathlib.Path(__file__).parent / 'files' / 'tags-cli.csv'
|
csv = pathlib.Path(__file__).parent / 'files' / 'tags-cli.csv'
|
||||||
|
@ -232,7 +247,8 @@ def test_tag_multiple_secondary_org(user: UserClient):
|
||||||
user.post({'id': 'foo1', 'secondary': 'bar'}, res=Tag, status=UniqueViolation)
|
user.post({'id': 'foo1', 'secondary': 'bar'}, res=Tag, status=UniqueViolation)
|
||||||
|
|
||||||
|
|
||||||
def test_crate_num_regular_tags(user: UserClient, requests_mock: requests_mock.mocker.Mocker):
|
@pytest.mark.mvp
|
||||||
|
def test_create_num_regular_tags(user: UserClient, requests_mock: requests_mock.mocker.Mocker):
|
||||||
"""Create regular tags. This is done using a tag provider that
|
"""Create regular tags. This is done using a tag provider that
|
||||||
returns IDs. These tags are printable.
|
returns IDs. These tags are printable.
|
||||||
"""
|
"""
|
||||||
|
@ -252,6 +268,7 @@ def test_crate_num_regular_tags(user: UserClient, requests_mock: requests_mock.m
|
||||||
assert data['items'][1]['printable']
|
assert data['items'][1]['printable']
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_get_tags_endpoint(user: UserClient, app: Devicehub,
|
def test_get_tags_endpoint(user: UserClient, app: Devicehub,
|
||||||
requests_mock: requests_mock.mocker.Mocker):
|
requests_mock: requests_mock.mocker.Mocker):
|
||||||
"""Performs GET /tags after creating 3 tags, 2 printable and one
|
"""Performs GET /tags after creating 3 tags, 2 printable and one
|
||||||
|
|
|
@ -16,6 +16,7 @@ from ereuse_devicehub.resources.user.models import User
|
||||||
from tests.conftest import app_context, create_user
|
from tests.conftest import app_context, create_user
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(app_context.__name__)
|
@pytest.mark.usefixtures(app_context.__name__)
|
||||||
def test_create_user_method_with_agent(app: Devicehub):
|
def test_create_user_method_with_agent(app: Devicehub):
|
||||||
"""Tests creating an user through the main method.
|
"""Tests creating an user through the main method.
|
||||||
|
@ -41,6 +42,7 @@ def test_create_user_method_with_agent(app: Devicehub):
|
||||||
assert individual.email == user.email
|
assert individual.email == user.email
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(app_context.__name__)
|
@pytest.mark.usefixtures(app_context.__name__)
|
||||||
def test_create_user_email_insensitive():
|
def test_create_user_email_insensitive():
|
||||||
"""Ensures email is case insensitive."""
|
"""Ensures email is case insensitive."""
|
||||||
|
@ -53,6 +55,7 @@ def test_create_user_email_insensitive():
|
||||||
assert u1.email == 'foo@foo.com'
|
assert u1.email == 'foo@foo.com'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(app_context.__name__)
|
@pytest.mark.usefixtures(app_context.__name__)
|
||||||
def test_hash_password():
|
def test_hash_password():
|
||||||
"""Tests correct password hashing and equaling."""
|
"""Tests correct password hashing and equaling."""
|
||||||
|
@ -61,6 +64,7 @@ def test_hash_password():
|
||||||
assert user.password == 'foo'
|
assert user.password == 'foo'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_login_success(client: Client, app: Devicehub):
|
def test_login_success(client: Client, app: Devicehub):
|
||||||
"""Tests successfully performing login.
|
"""Tests successfully performing login.
|
||||||
This checks that:
|
This checks that:
|
||||||
|
@ -83,6 +87,7 @@ def test_login_success(client: Client, app: Devicehub):
|
||||||
assert user['inventories'][0]['id'] == 'test'
|
assert user['inventories'][0]['id'] == 'test'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_login_failure(client: Client, app: Devicehub):
|
def test_login_failure(client: Client, app: Devicehub):
|
||||||
"""Tests performing wrong login."""
|
"""Tests performing wrong login."""
|
||||||
# Wrong password
|
# Wrong password
|
||||||
|
|
|
@ -7,13 +7,14 @@ import pytest
|
||||||
|
|
||||||
from ereuse_devicehub.client import UserClient
|
from ereuse_devicehub.client import UserClient
|
||||||
from ereuse_devicehub.resources.action import models as em
|
from ereuse_devicehub.resources.action import models as em
|
||||||
from ereuse_devicehub.resources.action.models import RateComputer, VisualTest
|
from ereuse_devicehub.resources.action.models import RateComputer, BenchmarkProcessor, BenchmarkRamSysbench
|
||||||
from ereuse_devicehub.resources.device.exceptions import NeedsId
|
from ereuse_devicehub.resources.device.exceptions import NeedsId
|
||||||
from ereuse_devicehub.resources.device.models import Device
|
from ereuse_devicehub.resources.device.models import Device
|
||||||
from ereuse_devicehub.resources.tag.model import Tag
|
from ereuse_devicehub.resources.tag.model import Tag
|
||||||
from tests.conftest import file
|
from tests.conftest import file
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_workbench_server_condensed(user: UserClient):
|
def test_workbench_server_condensed(user: UserClient):
|
||||||
"""As :def:`.test_workbench_server_phases` but all the actions
|
"""As :def:`.test_workbench_server_phases` but all the actions
|
||||||
condensed in only one big ``Snapshot`` file, as described
|
condensed in only one big ``Snapshot`` file, as described
|
||||||
|
@ -36,6 +37,7 @@ def test_workbench_server_condensed(user: UserClient):
|
||||||
('BenchmarkProcessorSysbench', 5),
|
('BenchmarkProcessorSysbench', 5),
|
||||||
('StressTest', 1),
|
('StressTest', 1),
|
||||||
('EraseSectors', 6),
|
('EraseSectors', 6),
|
||||||
|
('EreusePrice', 1),
|
||||||
('BenchmarkRamSysbench', 1),
|
('BenchmarkRamSysbench', 1),
|
||||||
('BenchmarkProcessor', 5),
|
('BenchmarkProcessor', 5),
|
||||||
('Install', 6),
|
('Install', 6),
|
||||||
|
@ -43,7 +45,6 @@ def test_workbench_server_condensed(user: UserClient):
|
||||||
('BenchmarkDataStorage', 6),
|
('BenchmarkDataStorage', 6),
|
||||||
('BenchmarkDataStorage', 7),
|
('BenchmarkDataStorage', 7),
|
||||||
('TestDataStorage', 6),
|
('TestDataStorage', 6),
|
||||||
('VisualTest', 1),
|
|
||||||
('RateComputer', 1)
|
('RateComputer', 1)
|
||||||
}
|
}
|
||||||
assert snapshot['closed']
|
assert snapshot['closed']
|
||||||
|
@ -58,15 +59,14 @@ def test_workbench_server_condensed(user: UserClient):
|
||||||
assert device['ramSize'] == 2048, 'There are 3 RAM: 2 x 1024 and 1 None sizes'
|
assert device['ramSize'] == 2048, 'There are 3 RAM: 2 x 1024 and 1 None sizes'
|
||||||
assert device['rate']['closed']
|
assert device['rate']['closed']
|
||||||
assert device['rate']['severity'] == 'Info'
|
assert device['rate']['severity'] == 'Info'
|
||||||
assert device['rate']['rating'] == 0
|
assert device['rate']['rating'] == 1
|
||||||
assert device['rate']['type'] == RateComputer.t
|
assert device['rate']['type'] == RateComputer.t
|
||||||
# TODO JN why haven't same order in actions??
|
# TODO JN why haven't same order in actions on each execution?
|
||||||
assert device['actions'][2]['type'] == VisualTest.t
|
assert device['actions'][2]['type'] == BenchmarkProcessor.t or device['actions'][2]['type'] == BenchmarkRamSysbench.t
|
||||||
assert device['actions'][2]['appearanceRange'] == 'A'
|
|
||||||
assert device['actions'][2]['functionalityRange'] == 'B'
|
|
||||||
assert device['tags'][0]['id'] == 'tag1'
|
assert device['tags'][0]['id'] == 'tag1'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
@pytest.mark.xfail(reason='Functionality not yet developed.')
|
@pytest.mark.xfail(reason='Functionality not yet developed.')
|
||||||
def test_workbench_server_phases(user: UserClient):
|
def test_workbench_server_phases(user: UserClient):
|
||||||
"""Tests the phases described in the docs section `Snapshots from
|
"""Tests the phases described in the docs section `Snapshots from
|
||||||
|
@ -134,6 +134,7 @@ def test_workbench_server_phases(user: UserClient):
|
||||||
assert len(pc['actions']) == 10 # todo shall I add child actions?
|
assert len(pc['actions']) == 10 # todo shall I add child actions?
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_real_hp_11(user: UserClient):
|
def test_real_hp_11(user: UserClient):
|
||||||
s = file('real-hp.snapshot.11')
|
s = file('real-hp.snapshot.11')
|
||||||
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
||||||
|
@ -160,11 +161,13 @@ def test_real_hp_11(user: UserClient):
|
||||||
# todo check rating
|
# todo check rating
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_real_toshiba_11(user: UserClient):
|
def test_real_toshiba_11(user: UserClient):
|
||||||
s = file('real-toshiba.snapshot.11')
|
s = file('real-toshiba.snapshot.11')
|
||||||
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient):
|
def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient):
|
||||||
"""Checks the values of the device, components,
|
"""Checks the values of the device, components,
|
||||||
actions and their relationships of a real pc.
|
actions and their relationships of a real pc.
|
||||||
|
@ -186,20 +189,13 @@ def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient):
|
||||||
# assert pc['actions'][0]['functionalityRange'] == 'B'
|
# assert pc['actions'][0]['functionalityRange'] == 'B'
|
||||||
# TODO add appearance and functionality Range in device[rate]
|
# TODO add appearance and functionality Range in device[rate]
|
||||||
|
|
||||||
assert rate['processorRange'] == 'VERY_LOW'
|
assert rate['processorRange'] == 'LOW'
|
||||||
assert rate['ramRange'] == 'VERY_LOW'
|
assert rate['ramRange'] == 'LOW'
|
||||||
assert rate['ratingRange'] == 'VERY_LOW'
|
assert rate['ratingRange'] == 'LOW'
|
||||||
assert rate['ram'] == 1.53
|
assert rate['ram'] == 1.53
|
||||||
# TODO add camelCase instead of snake_case
|
# TODO add camelCase instead of snake_case
|
||||||
assert rate['dataStorage'] == 3.76
|
assert rate['dataStorage'] == 3.76
|
||||||
assert rate['type'] == 'RateComputer'
|
assert rate['type'] == 'RateComputer'
|
||||||
# TODO change pc[actions] TestBios instead of rate[biosRange]
|
|
||||||
# assert rate['biosRange'] == 'C'
|
|
||||||
assert rate['appearance'] == 0, 'appearance B equals 0 points'
|
|
||||||
# todo fix gets correctly functionality rates values not equals to 0.
|
|
||||||
assert rate['functionality'] == 0, 'functionality A equals 0.4 points'
|
|
||||||
# why this assert?? -2 < rating < 4.7
|
|
||||||
# assert rate['rating'] > 0 and rate['rating'] != 1
|
|
||||||
components = snapshot['components']
|
components = snapshot['components']
|
||||||
wifi = components[0]
|
wifi = components[0]
|
||||||
assert wifi['hid'] == 'networkadapter-qualcomm_atheros-' \
|
assert wifi['hid'] == 'networkadapter-qualcomm_atheros-' \
|
||||||
|
@ -233,7 +229,7 @@ def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient):
|
||||||
assert em.BenchmarkRamSysbench.t in action_types
|
assert em.BenchmarkRamSysbench.t in action_types
|
||||||
assert em.StressTest.t in action_types
|
assert em.StressTest.t in action_types
|
||||||
assert em.Snapshot.t in action_types
|
assert em.Snapshot.t in action_types
|
||||||
assert len(actions) == 7
|
assert len(actions) == 8
|
||||||
gpu = components[3]
|
gpu = components[3]
|
||||||
assert gpu['model'] == 'atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller'
|
assert gpu['model'] == 'atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller'
|
||||||
assert gpu['manufacturer'] == 'intel corporation'
|
assert gpu['manufacturer'] == 'intel corporation'
|
||||||
|
@ -243,8 +239,7 @@ def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient):
|
||||||
assert em.BenchmarkRamSysbench.t in action_types
|
assert em.BenchmarkRamSysbench.t in action_types
|
||||||
assert em.StressTest.t in action_types
|
assert em.StressTest.t in action_types
|
||||||
assert em.Snapshot.t in action_types
|
assert em.Snapshot.t in action_types
|
||||||
# todo why?? change action types 3 to 5
|
assert len(action_types) == 6
|
||||||
assert len(action_types) == 5
|
|
||||||
sound = components[4]
|
sound = components[4]
|
||||||
assert sound['model'] == 'nm10/ich7 family high definition audio controller'
|
assert sound['model'] == 'nm10/ich7 family high definition audio controller'
|
||||||
sound = components[5]
|
sound = components[5]
|
||||||
|
@ -266,8 +261,7 @@ def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient):
|
||||||
assert em.TestDataStorage.t in action_types
|
assert em.TestDataStorage.t in action_types
|
||||||
assert em.EraseBasic.t in action_types
|
assert em.EraseBasic.t in action_types
|
||||||
assert em.Snapshot.t in action_types
|
assert em.Snapshot.t in action_types
|
||||||
# todo why?? change action types 6 to 8
|
assert len(action_types) == 9
|
||||||
assert len(action_types) == 8
|
|
||||||
erase = next(e for e in hdd['actions'] if e['type'] == em.EraseBasic.t)
|
erase = next(e for e in hdd['actions'] if e['type'] == em.EraseBasic.t)
|
||||||
assert erase['endTime']
|
assert erase['endTime']
|
||||||
assert erase['startTime']
|
assert erase['startTime']
|
||||||
|
@ -277,17 +271,20 @@ def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient):
|
||||||
assert mother['hid'] == 'motherboard-asustek_computer_inc-1001pxd-eee0123456789'
|
assert mother['hid'] == 'motherboard-asustek_computer_inc-1001pxd-eee0123456789'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_real_custom(user: UserClient):
|
def test_real_custom(user: UserClient):
|
||||||
s = file('real-custom.snapshot.11')
|
s = file('real-custom.snapshot.11')
|
||||||
snapshot, _ = user.post(res=em.Snapshot, data=s, status=NeedsId)
|
snapshot, _ = user.post(res=em.Snapshot, data=s, status=NeedsId)
|
||||||
# todo insert with tag
|
# todo insert with tag
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_real_hp_quad_core(user: UserClient):
|
def test_real_hp_quad_core(user: UserClient):
|
||||||
s = file('real-hp-quad-core.snapshot.11')
|
s = file('real-hp-quad-core.snapshot.11')
|
||||||
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_real_eee_1000h(user: UserClient):
|
def test_real_eee_1000h(user: UserClient):
|
||||||
s = file('asus-eee-1000h.snapshot.11')
|
s = file('asus-eee-1000h.snapshot.11')
|
||||||
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
||||||
|
@ -305,6 +302,7 @@ SNAPSHOTS_NEED_ID = {
|
||||||
"""Snapshots that do not generate HID requiring a custom ID."""
|
"""Snapshots that do not generate HID requiring a custom ID."""
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(reason='It needs to be fixed.')
|
||||||
@pytest.mark.parametrize('file',
|
@pytest.mark.parametrize('file',
|
||||||
(pytest.param(f, id=f.name)
|
(pytest.param(f, id=f.name)
|
||||||
for f in pathlib.Path(__file__).parent.joinpath('workbench_files').iterdir())
|
for f in pathlib.Path(__file__).parent.joinpath('workbench_files').iterdir())
|
||||||
|
@ -320,12 +318,14 @@ def test_workbench_fixtures(file: pathlib.Path, user: UserClient):
|
||||||
status=201 if file.name not in SNAPSHOTS_NEED_ID else NeedsId)
|
status=201 if file.name not in SNAPSHOTS_NEED_ID else NeedsId)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_workbench_asus_1001pxd_rate_low(user: UserClient):
|
def test_workbench_asus_1001pxd_rate_low(user: UserClient):
|
||||||
"""Tests an Asus 1001pxd with a low rate."""
|
"""Tests an Asus 1001pxd with a low rate."""
|
||||||
s = file('asus-1001pxd.snapshot')
|
s = file('asus-1001pxd.snapshot')
|
||||||
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
def test_david(user: UserClient):
|
def test_david(user: UserClient):
|
||||||
s = file('david.lshw.snapshot')
|
s = file('david.lshw.snapshot')
|
||||||
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
||||||
|
|
Reference in a new issue