Merge branch 'testing' into feature/27-permission-posting-action
This commit is contained in:
commit
8fd2e06f0a
|
@ -63,5 +63,5 @@ jobs:
|
|||
|
||||
- name: Run Tests
|
||||
run: |
|
||||
pytest --maxfail=5 tests/
|
||||
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>`__.
|
||||
|
||||
Install Devicehub with *pip*:
|
||||
``pip3 install ereuse-devicehub -U --pre``.
|
||||
``pip3 install -U -r requirements.txt -e .``.
|
||||
|
||||
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-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
|
||||
``ereuse``.
|
||||
|
||||
Create the tables in the database by executing in the same directory
|
||||
where ``app.py`` is:
|
||||
Using the `dh` tool for set up with one or multiple inventories.
|
||||
Create the tables in the database by executing:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
$ flask init-db
|
||||
$ export dhi=dbtest; dh inv add --common --name dbtest
|
||||
|
||||
Finally, run the app:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
$ flask run
|
||||
|
||||
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.
|
||||
$ export dhi=dbtest;dh run --debugger
|
||||
|
||||
The error ‘bdist_wheel’ can happen when you work with a *virtual environment*.
|
||||
To fix it, install in the *virtual environment* wheel
|
||||
|
@ -70,9 +57,14 @@ package. ``pip3 install wheel``
|
|||
Multiple instances
|
||||
------------------
|
||||
Devicehub can run as a single inventory or with multiple inventories,
|
||||
each inventory being an instance of the ``devicehub``. To execute
|
||||
one instance, use the ``flask`` command, to execute multiple instances
|
||||
use the ``dh`` command. The ``dh`` command is like ``flask``, but
|
||||
each inventory being an instance of the ``devicehub``. To add a new inventory
|
||||
execute:
|
||||
|
||||
.. 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
|
||||
directly.
|
||||
|
||||
|
@ -86,6 +78,68 @@ Testing
|
|||
password ``ereuse``.
|
||||
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
|
||||
*******************
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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_sqlalchemy import SQLAlchemy
|
||||
from teal.teal import Teal
|
||||
from teal.db import SchemaSQLAlchemy
|
||||
|
||||
from ereuse_devicehub.auth import Auth
|
||||
from ereuse_devicehub.client import Client
|
||||
|
@ -115,6 +116,16 @@ class Devicehub(Teal):
|
|||
self.db.session.commit()
|
||||
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 {}?'
|
||||
.format(os.environ.get('dhi')))
|
||||
def delete_inventory(self):
|
||||
|
|
|
@ -77,10 +77,12 @@ class Dummy:
|
|||
runner.invoke('tag', 'add', id,
|
||||
'-p', 'https://t.devicetag.io',
|
||||
'-s', sec,
|
||||
'-u', user1.user["id"],
|
||||
'-o', org_id)
|
||||
# create tag for pc-laudem
|
||||
runner.invoke('tag', 'add', 'tagA',
|
||||
'-p', 'https://t.devicetag.io',
|
||||
'-u', user1.user["id"],
|
||||
'-s', 'tagA-secondary')
|
||||
files = tuple(Path(__file__).parent.joinpath('files').iterdir())
|
||||
print('done.')
|
||||
|
|
|
@ -20,9 +20,6 @@ device:
|
|||
- type: Tag
|
||||
id: tag1
|
||||
actions:
|
||||
- type: VisualTest
|
||||
appearanceRange: A
|
||||
functionalityRange: B
|
||||
- type: BenchmarkRamSysbench
|
||||
rate: 2444
|
||||
elapsed: 1
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Generic single-database configuration.
|
|
@ -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()
|
|
@ -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()}')
|
File diff suppressed because one or more lines are too long
|
@ -267,6 +267,7 @@ class MigrateFromDef(ActionDef):
|
|||
VIEW = None
|
||||
SCHEMA = schemas.MigrateFrom
|
||||
|
||||
|
||||
class TransferredDef(ActionDef):
|
||||
VIEW = None
|
||||
SCHEMA = schemas.Transferred
|
|
@ -1423,6 +1423,7 @@ class DisposeProduct(Trade):
|
|||
# performing :class:`.ToDispose` + :class:`.Receive` to a
|
||||
# ``RecyclingCenter``.
|
||||
|
||||
|
||||
class TransferOwnershipBlockchain(Trade):
|
||||
""" 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):
|
||||
pass
|
||||
|
||||
|
||||
class Transferred(ActionWithMultipleDevices):
|
||||
"""Transferred through blockchain."""
|
||||
pass
|
|
@ -12,7 +12,7 @@ from ereuse_devicehub.resources.action.models import Action, RateComputer, Snaps
|
|||
InitTransfer
|
||||
from ereuse_devicehub.resources.action.rate.v1_0 import CannotRate
|
||||
from ereuse_devicehub.resources.device.models import Component, Computer
|
||||
from ereuse_devicehub.resources.enums import SnapshotSoftware
|
||||
from ereuse_devicehub.resources.enums import SnapshotSoftware, Severity
|
||||
from ereuse_devicehub.resources.user.exceptions import InsufficientPermission
|
||||
|
||||
|
||||
|
@ -107,7 +107,9 @@ class ActionView(View):
|
|||
snapshot.actions.add(price)
|
||||
elif snapshot.software == SnapshotSoftware.WorkbenchAndroid:
|
||||
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().final_flush()
|
||||
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.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):
|
||||
|
@ -26,6 +26,13 @@ class DeviceDef(Resource):
|
|||
super().__init__(app, import_name, static_folder, static_url_path, template_folder,
|
||||
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):
|
||||
VIEW = None
|
||||
|
|
|
@ -52,7 +52,7 @@ class Device(Thing):
|
|||
"""
|
||||
type = Column(Unicode(STR_SM_SIZE), nullable=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
|
||||
from Devicehub using literal identifiers from the device,
|
||||
so it can re-generated *offline*.
|
||||
|
|
|
@ -12,7 +12,6 @@ from teal.marshmallow import ValidationError
|
|||
|
||||
from ereuse_devicehub.db import db
|
||||
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.tag.model import Tag
|
||||
|
||||
|
@ -151,9 +150,6 @@ class Sync:
|
|||
"""
|
||||
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'
|
||||
if not device.tags and not device.hid:
|
||||
# We cannot identify this device
|
||||
raise NeedsId()
|
||||
db_device = None
|
||||
if device.hid:
|
||||
with suppress(ResourceNotFound):
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import datetime
|
||||
import uuid
|
||||
from itertools import filterfalse
|
||||
|
||||
import marshmallow
|
||||
from flask import current_app as app, render_template, request, Response
|
||||
from flask.json import jsonify
|
||||
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.cache import cache
|
||||
from teal.resource import View
|
||||
|
@ -19,6 +22,7 @@ from ereuse_devicehub.resources.device.models import Device, Manufacturer, Compu
|
|||
from ereuse_devicehub.resources.device.search import DeviceSearch
|
||||
from ereuse_devicehub.resources.lot.models import LotDeviceDescendants
|
||||
from ereuse_devicehub.resources.tag.model import Tag
|
||||
from ereuse_devicehub.resources.enums import SnapshotSoftware
|
||||
|
||||
|
||||
class OfType(f.Str):
|
||||
|
@ -152,6 +156,67 @@ class DeviceView(View):
|
|||
return query.filter(*args['filter']).order_by(*args['sort'])
|
||||
|
||||
|
||||
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 FindArgs(marshmallow.Schema):
|
||||
search = marshmallow.fields.Str(required=True,
|
||||
|
|
|
@ -43,7 +43,10 @@ class DeviceRow(OrderedDict):
|
|||
self['Trading state'] = device.last_action_of(*states.Trading.actions()).t
|
||||
except:
|
||||
self['Trading state'] = ''
|
||||
self['Price'] = device.price.price or ''
|
||||
try:
|
||||
self['Price'] = device.price
|
||||
except:
|
||||
self['Price'] = ''
|
||||
if isinstance(device, d.Computer):
|
||||
self['Processor'] = device.processor_model
|
||||
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
|
||||
for type in sorted(current_app.resources[d.Component.t].subresources_types): # type: str
|
||||
max = self.NUMS.get(type, 4)
|
||||
if type not in ['Component', 'HardDrive', 'SolidStateDrive', 'Camera', 'Battery']:
|
||||
if type not in ['Component', 'HardDrive', 'SolidStateDrive']:
|
||||
i = 1
|
||||
for component in (r for r in self.device.components if r.type == type):
|
||||
self.fill_component(type, i, component)
|
||||
|
|
|
@ -18,6 +18,7 @@ class TagDef(Resource):
|
|||
VIEW = TagView
|
||||
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. '
|
||||
'By default the organization operating this Devicehub.'
|
||||
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,
|
||||
methods={'PUT'})
|
||||
|
||||
@option('-u', '--owner', help=OWNER_H)
|
||||
@option('-o', '--org', help=ORG_H)
|
||||
@option('-p', '--provider', help=PROV_H)
|
||||
@option('-s', '--sec', help=Tag.secondary.comment)
|
||||
|
@ -55,18 +57,19 @@ class TagDef(Resource):
|
|||
def create_tag(self,
|
||||
id: str,
|
||||
org: str = None,
|
||||
owner: str = None,
|
||||
sec: str = None,
|
||||
provider: str = None):
|
||||
"""Create a tag with the given ID."""
|
||||
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()
|
||||
|
||||
@option('--org', help=ORG_H)
|
||||
@option('--provider', help=PROV_H)
|
||||
@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.
|
||||
|
||||
CSV must have the following columns:
|
||||
|
@ -77,6 +80,6 @@ class TagDef(Resource):
|
|||
with path.open() as f:
|
||||
for id, sec in csv.reader(f):
|
||||
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()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from contextlib import suppress
|
||||
from typing import Set
|
||||
|
||||
from flask import g
|
||||
from boltons import urlutils
|
||||
from sqlalchemy import BigInteger, Column, ForeignKey, UniqueConstraint
|
||||
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.resources.agent.models import Organization
|
||||
from ereuse_devicehub.resources.device.models import Device
|
||||
from ereuse_devicehub.resources.user.models import User
|
||||
from ereuse_devicehub.resources.models import Thing
|
||||
|
||||
|
||||
|
@ -26,6 +28,11 @@ class Tags(Set['Tag']):
|
|||
class Tag(Thing):
|
||||
id = Column(db.CIText(), primary_key=True)
|
||||
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),
|
||||
ForeignKey(Organization.id),
|
||||
primary_key=True,
|
||||
|
|
|
@ -3,6 +3,7 @@ from sqlalchemy.util import OrderedSet
|
|||
from teal.marshmallow import SanitizedStr, URL
|
||||
|
||||
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.device.schemas import Device
|
||||
from ereuse_devicehub.resources.schemas import Thing
|
||||
|
@ -22,6 +23,7 @@ class Tag(Thing):
|
|||
provider = URL(description=m.Tag.provider.comment,
|
||||
validator=without_slash)
|
||||
device = NestedOn(Device, dump_only=True)
|
||||
owner = NestedOn(User, only_query='id')
|
||||
org = NestedOn(Organization, collection_class=OrderedSet, only_query='id')
|
||||
secondary = SanitizedStr(lower=True, description=m.Tag.secondary.comment)
|
||||
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 ereuse_devicehub.db import db
|
||||
from ereuse_devicehub import auth
|
||||
from ereuse_devicehub.query import things_response
|
||||
from ereuse_devicehub.resources.device.models import Device
|
||||
from ereuse_devicehub.resources.tag import Tag
|
||||
|
||||
|
||||
class TagView(View):
|
||||
@auth.Auth.requires_auth
|
||||
def post(self):
|
||||
"""Creates a tag."""
|
||||
num = request.args.get('num', type=int)
|
||||
|
@ -19,8 +21,10 @@ class TagView(View):
|
|||
res = self._post_one()
|
||||
return res
|
||||
|
||||
@auth.Auth.requires_auth
|
||||
def find(self, args: dict):
|
||||
tags = Tag.query.filter(Tag.is_printable_q()) \
|
||||
.filter_by(owner=g.user) \
|
||||
.order_by(Tag.created.desc()) \
|
||||
.paginate(per_page=200) # type: Pagination
|
||||
return things_response(
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
alembic==1.4.2
|
||||
anytree==2.4.3
|
||||
apispec==0.39.0
|
||||
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
|
||||
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,,,,,,,,,,,,,,,,,,,,
|
||||
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,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
|
|
|
@ -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
|
||||
|
|
|
|
@ -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
|
||||
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
|
||||
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,,,,,,,,,,,,,,,,,,,,
|
||||
Keyboard,,,,,bar,foo,baz,Tue Jul 2 10:38:14 2019,
|
||||
ComputerMonitor,,,,,cn0fp446728728541c8s,1707fpf,dell,Tue Jul 2 10:38:15 2019,
|
||||
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,,,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,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,,,
|
||||
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
|
||||
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
|
||||
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,,,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
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||
def test_author():
|
||||
"""Checks the default created author.
|
||||
|
@ -36,6 +37,7 @@ def test_author():
|
|||
assert e.author == user
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||
def test_erase_basic():
|
||||
erasure = models.EraseBasic(
|
||||
|
@ -54,6 +56,7 @@ def test_erase_basic():
|
|||
assert not erasure.standards, 'EraseBasic themselves do not have standards'
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||
def test_validate_device_data_storage():
|
||||
"""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__)
|
||||
def test_erase_sectors_steps_erasure_standards_hmg_is5():
|
||||
erasure = models.EraseSectors(
|
||||
|
@ -89,6 +93,7 @@ def test_erase_sectors_steps_erasure_standards_hmg_is5():
|
|||
assert {enums.ErasureStandards.HMG_IS5} == erasure.standards
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||
def test_test_data_storage_working():
|
||||
"""Tests TestDataStorage with the resulting properties in Device."""
|
||||
|
@ -121,6 +126,7 @@ def test_test_data_storage_working():
|
|||
assert hdd.problems == []
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||
def test_install():
|
||||
hdd = HardDrive(serial_number='sn')
|
||||
|
@ -131,6 +137,7 @@ def test_install():
|
|||
db.session.commit()
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||
def test_update_components_action_one():
|
||||
computer = Desktop(serial_number='sn1',
|
||||
|
@ -159,6 +166,7 @@ def test_update_components_action_one():
|
|||
assert len(test.components) == 1
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||
def test_update_components_action_multiple():
|
||||
computer = Desktop(serial_number='sn1',
|
||||
|
@ -188,6 +196,7 @@ def test_update_components_action_multiple():
|
|||
assert ready.components
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||
def test_update_parent():
|
||||
computer = Desktop(serial_number='sn1',
|
||||
|
@ -208,6 +217,7 @@ def test_update_parent():
|
|||
assert not benchmark.parent
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.parametrize('action_model_state',
|
||||
(pytest.param(ams, id=ams[0].__class__.__name__)
|
||||
for ams in [
|
||||
|
@ -230,6 +240,7 @@ def test_generic_action(action_model_state: Tuple[models.Action, states.Trading]
|
|||
assert device['physical'] == state.name
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||
def test_live():
|
||||
"""Tests inserting a Live into the database and GETting it."""
|
||||
|
@ -255,18 +266,7 @@ def test_live():
|
|||
assert device['physical'] == states.Physical.InUse.name
|
||||
|
||||
|
||||
@pytest.mark.xfail(reson='Functionality not developed.')
|
||||
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.mvp
|
||||
@pytest.mark.parametrize('action_model_state',
|
||||
(pytest.param(ams, id=ams[0].__name__)
|
||||
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
|
||||
|
||||
|
||||
@pytest.mark.xfail(reson='Develop migrate')
|
||||
def test_migrate():
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||
def test_price_custom():
|
||||
computer = Desktop(serial_number='sn1', model='ml1', manufacturer='mr1',
|
||||
|
@ -322,6 +318,7 @@ def test_price_custom():
|
|||
assert c['price']['id'] == p['id']
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_price_custom_client(user: UserClient):
|
||||
"""As test_price_custom but creating the price through the API."""
|
||||
s = file('basic.snapshot')
|
||||
|
@ -339,16 +336,7 @@ def test_price_custom_client(user: UserClient):
|
|||
assert 25 == device['price']['price']
|
||||
|
||||
|
||||
@pytest.mark.xfail(reson='Develop test')
|
||||
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.mvp
|
||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||
def test_erase_physical():
|
||||
erasure = models.ErasePhysical(
|
||||
|
@ -357,27 +345,3 @@ def test_erase_physical():
|
|||
)
|
||||
db.session.add(erasure)
|
||||
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
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_authenticate_success(app: Devicehub):
|
||||
"""Checks the authenticate method."""
|
||||
with app.app_context():
|
||||
|
@ -16,6 +17,7 @@ def test_authenticate_success(app: Devicehub):
|
|||
assert response_user == user
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_authenticate_error(app: Devicehub):
|
||||
"""Tests the authenticate method with wrong token values."""
|
||||
with app.app_context():
|
||||
|
@ -29,6 +31,7 @@ def test_authenticate_error(app: Devicehub):
|
|||
app.auth.authenticate(token='this is a wrong uuid')
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_auth_view(user: UserClient, client: Client):
|
||||
"""Tests authentication at endpoint / view."""
|
||||
user.get(res='User', item=user.user['id'], status=200)
|
||||
|
|
|
@ -1,8 +1,19 @@
|
|||
import pytest
|
||||
|
||||
from ereuse_devicehub.devicehub import Devicehub
|
||||
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():
|
||||
with pytest.raises(ImportError):
|
||||
# Simplejson has a different signature than stdlib json
|
||||
|
@ -12,6 +23,7 @@ def test_dependencies():
|
|||
|
||||
|
||||
# noinspection PyArgumentList
|
||||
@pytest.mark.mvp
|
||||
def test_api_docs(client: Client):
|
||||
"""Tests /apidocs correct initialization."""
|
||||
docs, _ = client.get('/apidocs')
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import datetime
|
||||
from uuid import UUID
|
||||
|
||||
import pytest
|
||||
from teal.db import UniqueViolation
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_unique_violation():
|
||||
class IntegrityErrorMock:
|
||||
def __init__(self) -> None:
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import datetime
|
||||
from uuid import UUID
|
||||
from flask import g
|
||||
|
||||
import pytest
|
||||
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, \
|
||||
Sync
|
||||
from ereuse_devicehub.resources.enums import ComputerChassis, DisplayTech, Severity, \
|
||||
SnapshotSoftware
|
||||
SnapshotSoftware, TransferState
|
||||
from ereuse_devicehub.resources.tag.model import Tag
|
||||
from ereuse_devicehub.resources.user import User
|
||||
from tests import conftest
|
||||
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():
|
||||
"""Tests that the correctness of the device model and its relationships."""
|
||||
pc = d.Desktop(model='p1mo',
|
||||
|
@ -76,6 +78,7 @@ def test_device_problems():
|
|||
pass
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||
def test_device_schema():
|
||||
"""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))
|
||||
|
||||
|
||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||
def test_physical_properties():
|
||||
c = d.Motherboard(slots=2,
|
||||
usb=3,
|
||||
|
@ -118,14 +122,21 @@ def test_physical_properties():
|
|||
'ram_slots': None
|
||||
}
|
||||
assert pc.physical_properties == {
|
||||
'model': 'foo',
|
||||
'chassis': ComputerChassis.Tower,
|
||||
'deliverynote_address': None,
|
||||
'deposit': 0,
|
||||
'ethereum_address': None,
|
||||
'manufacturer': 'bar',
|
||||
'model': 'foo',
|
||||
'owner_id': pc.owner_id,
|
||||
'receiver_id': None,
|
||||
'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():
|
||||
snapshot = conftest.file('pc-components.db')
|
||||
pc = snapshot['device']
|
||||
|
@ -147,7 +158,8 @@ def test_component_similar_one():
|
|||
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():
|
||||
# Original state:
|
||||
# pc has c1 and c2
|
||||
|
@ -178,7 +190,8 @@ def test_add_remove():
|
|||
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():
|
||||
"""Syncs a device that has an empty components list. The system should
|
||||
remove all the components from the device.
|
||||
|
@ -195,7 +208,8 @@ def test_sync_run_components_empty():
|
|||
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():
|
||||
"""Syncs a device that has a None components. The system should
|
||||
keep all the components from the device.
|
||||
|
@ -212,7 +226,8 @@ def test_sync_run_components_none():
|
|||
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():
|
||||
"""Syncs a new d.Desktop with HID and without a tag, creating it."""
|
||||
# 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
|
||||
|
||||
|
||||
@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():
|
||||
"""Syncs an existing d.Desktop with HID and without a tag."""
|
||||
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
|
||||
# 1: device exists on DB
|
||||
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
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||
def test_sync_execute_register_desktop_no_hid_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)
|
||||
|
||||
|
||||
@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():
|
||||
"""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'
|
||||
|
||||
|
||||
@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):
|
||||
"""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)
|
||||
pc = d.Desktop(**conftest.file('pc-components.db')['device'], tags=OrderedSet([tag]))
|
||||
db.session.add(g.user)
|
||||
returned_pc = Sync().execute_register(pc)
|
||||
db.session.commit()
|
||||
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'
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||
def test_sync_execute_register_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)
|
||||
|
||||
|
||||
@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():
|
||||
"""If the tag is linked to the device, regardless if it has HID,
|
||||
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'
|
||||
|
||||
|
||||
@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():
|
||||
"""Checks that sync raises an error if finds that at least two passed-in
|
||||
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)
|
||||
|
||||
|
||||
@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():
|
||||
"""Checks that sync raises an error if it finds that the HID 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)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.xfail(reason='It needs to be fixed.')
|
||||
def test_get_device(app: Devicehub, user: UserClient):
|
||||
"""Checks GETting a d.Desktop with its components."""
|
||||
with app.app_context():
|
||||
|
@ -398,6 +427,8 @@ def test_get_device(app: Devicehub, user: UserClient):
|
|||
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):
|
||||
"""Checks GETting multiple devices."""
|
||||
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():
|
||||
m = d.ComputerMonitor(technology=DisplayTech.LCD,
|
||||
manufacturer='foo',
|
||||
|
@ -439,6 +471,7 @@ def test_computer_monitor():
|
|||
db.session.commit()
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_manufacturer(user: UserClient):
|
||||
m, r = user.get(res='Manufacturer', query=[('search', '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()
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.xfail(reason='Develop functionality')
|
||||
def test_manufacturer_enforced():
|
||||
"""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):
|
||||
user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot)
|
||||
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'
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_device_public(user: UserClient, client: Client):
|
||||
s, _ = user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot)
|
||||
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
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||
def test_computer_accessory_model():
|
||||
sai = d.SAI()
|
||||
|
@ -493,6 +530,7 @@ def test_computer_accessory_model():
|
|||
db.session.commit()
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||
def test_networking_model():
|
||||
router = d.Router(speed=1000, wireless=True)
|
||||
|
|
|
@ -15,6 +15,7 @@ from tests import conftest
|
|||
from tests.conftest import file
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||
def test_device_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.'
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_device_query(user: UserClient):
|
||||
"""Checks result of inventory."""
|
||||
user.post(conftest.file('basic.snapshot'), res=Snapshot)
|
||||
|
@ -183,6 +185,7 @@ def test_device_query(user: UserClient):
|
|||
assert not pc['tags']
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_device_search_all_devices_token_if_empty(app: Devicehub, user: UserClient):
|
||||
"""Ensures DeviceSearch can regenerate itself when the table is empty."""
|
||||
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']
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_device_search_regenerate_table(app: DeviceSearch, user: UserClient):
|
||||
user.post(file('basic.snapshot'), res=Snapshot)
|
||||
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'
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_device_query_search(user: UserClient):
|
||||
# todo improve
|
||||
user.post(file('basic.snapshot'), res=Snapshot)
|
||||
|
@ -226,6 +231,7 @@ def test_device_query_search(user: UserClient):
|
|||
assert len(i['items']) == 1
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_device_query_search_synonyms_asus(user: UserClient):
|
||||
user.post(file('real-eee-1001pxd.snapshot.11'), res=Snapshot)
|
||||
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'])
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_device_query_search_synonyms_intel(user: UserClient):
|
||||
s = file('real-hp.snapshot.11')
|
||||
s['device']['model'] = 'foo' # The model had the word 'HP' in it
|
||||
|
|
|
@ -11,12 +11,14 @@ def noop():
|
|||
pass
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.fixture()
|
||||
def dispatcher(app: Devicehub, config: TestConfig) -> PathDispatcher:
|
||||
PathDispatcher.call = Mock(side_effect=lambda *args: args[0])
|
||||
return PathDispatcher(config_cls=config)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_dispatcher_default(dispatcher: PathDispatcher):
|
||||
"""The dispatcher returns not found for an URL that does not
|
||||
route to an app.
|
||||
|
@ -27,6 +29,7 @@ def test_dispatcher_default(dispatcher: PathDispatcher):
|
|||
assert app == PathDispatcher.NOT_FOUND
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_dispatcher_return_app(dispatcher: PathDispatcher):
|
||||
"""The dispatcher returns the correct app for the URL."""
|
||||
# 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'
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_dispatcher_users(dispatcher: PathDispatcher):
|
||||
"""Users special endpoint returns an app."""
|
||||
# For now returns the first app, as all apps
|
||||
|
|
|
@ -1,25 +1,31 @@
|
|||
import pytest
|
||||
import teal.marshmallow
|
||||
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.resources.action import models as e
|
||||
from ereuse_devicehub.resources.documents import documents as docs
|
||||
from ereuse_devicehub.resources.action.models import Snapshot
|
||||
from ereuse_devicehub.resources.documents import documents
|
||||
from tests.conftest import file
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_erasure_certificate_public_one(user: UserClient, client: Client):
|
||||
"""Public user can get certificate from one device as HTML or PDF."""
|
||||
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']),
|
||||
accept=ANY)
|
||||
assert 'html' in response.content_type
|
||||
assert '<html' 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']),
|
||||
query=[('format', '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')
|
||||
|
||||
doc, response = client.get(res=docs.DocumentDef.t,
|
||||
doc, response = client.get(res=documents.DocumentDef.t,
|
||||
item='erasures/{}'.format(erasure['id']),
|
||||
accept=ANY)
|
||||
assert 'html' in response.content_type
|
||||
|
@ -35,14 +41,15 @@ def test_erasure_certificate_public_one(user: UserClient, client: Client):
|
|||
assert '2018' in doc
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_erasure_certificate_private_query(user: UserClient):
|
||||
"""Logged-in user can get certificates using queries as HTML and
|
||||
PDF.
|
||||
"""
|
||||
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/',
|
||||
query=[('filter', {'id': [snapshot['device']['id']]})],
|
||||
accept=ANY)
|
||||
|
@ -50,7 +57,7 @@ def test_erasure_certificate_private_query(user: UserClient):
|
|||
assert '<html' in doc
|
||||
assert '2018' in doc
|
||||
|
||||
doc, response = user.get(res=docs.DocumentDef.t,
|
||||
doc, response = user.get(res=documents.DocumentDef.t,
|
||||
item='erasures/',
|
||||
query=[
|
||||
('filter', {'id': [snapshot['device']['id']]}),
|
||||
|
@ -60,6 +67,159 @@ def test_erasure_certificate_private_query(user: UserClient):
|
|||
assert 'application/pdf' == response.content_type
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
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)
|
||||
|
||||
|
||||
@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)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_inventory_create_delete_user(cli, tdb1, tdb2):
|
||||
"""Tests creating two inventories with users, one user has
|
||||
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')
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_create_existing_inventory(cli, tdb1):
|
||||
"""Tries to create twice the same inventory."""
|
||||
cli.inv('tdb1')
|
||||
|
|
|
@ -22,6 +22,7 @@ from tests import conftest
|
|||
"""
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||
def test_lot_model_children():
|
||||
"""Tests the property Lot.children
|
||||
|
@ -65,6 +66,7 @@ def test_lot_model_children():
|
|||
assert not l3.parents
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_lot_modify_patch_endpoint_and_delete(user: UserClient):
|
||||
"""Creates and modifies lot properties through the endpoint."""
|
||||
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)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||
def test_lot_device_relationship():
|
||||
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
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||
def test_lot_roots():
|
||||
"""Tests getting the method Lot.roots."""
|
||||
|
@ -298,6 +302,7 @@ def test_lot_roots():
|
|||
assert set(Lot.roots()) == {l1, l3}
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_post_get_lot(user: UserClient):
|
||||
"""Tests submitting and retreiving a basic 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'
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.xfail(reason='It needs to be fixed.')
|
||||
def test_lot_post_add_remove_device_view(app: Devicehub, user: UserClient):
|
||||
"""Tests adding a device to a lot using POST and
|
||||
removing it with DELETE.
|
||||
|
|
|
@ -15,6 +15,7 @@ from tests import conftest
|
|||
from tests.conftest import file
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||
def test_workbench_rate_db():
|
||||
rate = RateComputer(processor=0.1,
|
||||
|
@ -82,18 +83,19 @@ def test_price_from_rate():
|
|||
device=pc)
|
||||
_, price = RateComputer.compute(pc)
|
||||
|
||||
assert price.price == Decimal('92.2001')
|
||||
assert price.retailer.standard.amount == Decimal('40.9714')
|
||||
assert price.platform.standard.amount == Decimal('18.8434')
|
||||
assert price.refurbisher.standard.amount == Decimal('32.3853')
|
||||
assert price.price == Decimal('78.2001')
|
||||
assert price.retailer.standard.amount == Decimal('34.7502')
|
||||
assert price.platform.standard.amount == Decimal('15.9821')
|
||||
assert price.refurbisher.standard.amount == Decimal('27.4678')
|
||||
assert price.price >= price.retailer.standard.amount + price.platform.standard.amount \
|
||||
+ price.refurbisher.standard.amount
|
||||
assert price.retailer.warranty2.amount == Decimal('55.3085')
|
||||
assert price.platform.warranty2.amount == Decimal('25.4357')
|
||||
assert price.refurbisher.warranty2.amount == Decimal('43.7259')
|
||||
assert price.warranty2 == Decimal('124.47')
|
||||
assert price.retailer.warranty2.amount == Decimal('46.9103')
|
||||
assert price.platform.warranty2.amount == Decimal('21.5735')
|
||||
assert price.refurbisher.warranty2.amount == Decimal('37.0864')
|
||||
assert price.warranty2 == Decimal('105.57')
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_when_rate_must_not_compute(user: UserClient):
|
||||
"""Test to check if rate is computed in case of should not be calculated:
|
||||
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']
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||
def test_multiple_rates(user: UserClient):
|
||||
"""Tests submitting two rates from Workbench,
|
||||
|
@ -164,12 +167,12 @@ def test_multiple_rates(user: UserClient):
|
|||
assert rate1.processor == 3.95
|
||||
assert rate1.ram == 3.8
|
||||
|
||||
assert rate1.appearance == 0.3
|
||||
assert rate1.functionality == 0.4
|
||||
assert rate1.appearance is None
|
||||
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))
|
||||
ssd = SolidStateDrive(size=476940)
|
||||
|
@ -191,9 +194,9 @@ def test_multiple_rates(user: UserClient):
|
|||
assert rate2.processor == 3.78
|
||||
assert rate2.ram == 3.95
|
||||
|
||||
assert rate2.appearance == 0
|
||||
assert rate2.functionality == -0.5
|
||||
assert rate2.appearance is None
|
||||
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
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_rate_data_storage_rate():
|
||||
"""Test to check if compute data storage rate have same value than
|
||||
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)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_rate_data_storage_size_is_null():
|
||||
"""Test where input DataStorage.size = NULL, BenchmarkDataStorage.read_speed = 0,
|
||||
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
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_rate_no_data_storage():
|
||||
"""Test without data storage devices."""
|
||||
|
||||
|
@ -84,6 +87,7 @@ def test_rate_no_data_storage():
|
|||
assert data_storage_rate is None
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_rate_ram_rate():
|
||||
"""Test to check if compute ram rate have same value than previous
|
||||
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)'
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_rate_ram_rate_2modules():
|
||||
"""Test to check if compute ram rate have same value than previous
|
||||
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)'
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_rate_ram_rate_4modules():
|
||||
"""Test to check if compute ram rate have same value than previous
|
||||
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)'
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_rate_ram_module_size_is_0():
|
||||
"""Test where input data RamModule.size = 0; is like no RamModule
|
||||
has been detected.
|
||||
|
@ -135,6 +142,7 @@ def test_rate_ram_module_size_is_0():
|
|||
assert ram_rate is None
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_rate_ram_speed_is_null():
|
||||
"""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)'
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_rate_no_ram_module():
|
||||
"""Test without RamModule."""
|
||||
ram0 = RamModule()
|
||||
|
@ -159,6 +168,7 @@ def test_rate_no_ram_module():
|
|||
assert ram_rate is None
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_rate_processor_rate():
|
||||
"""Test to check if compute processor rate have same value than previous
|
||||
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)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_rate_processor_rate_2cores():
|
||||
"""Test to check if compute processor rate have same value than previous
|
||||
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)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_rate_processor_with_null_cores():
|
||||
"""Test with processor device have null number of cores."""
|
||||
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)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_rate_processor_with_null_speed():
|
||||
"""Test with processor device have null speed value."""
|
||||
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)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||
def test_rate_computer_1193():
|
||||
"""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.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__)
|
||||
def test_rate_computer_1201():
|
||||
"""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.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__)
|
||||
def test_rate_computer_multiple_ram_module():
|
||||
"""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 rate_pc.rating == 1.57
|
||||
assert rate_pc.rating == 1.37
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||
def test_rate_computer_one_ram_module():
|
||||
"""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.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')
|
||||
|
|
|
@ -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.devicehub import Devicehub
|
||||
from ereuse_devicehub.resources.action.models import Action, BenchmarkDataStorage, \
|
||||
BenchmarkProcessor, EraseSectors, RateComputer, Snapshot, SnapshotRequest, VisualTest, \
|
||||
MeasureBattery, BenchmarkRamSysbench, StressTest
|
||||
BenchmarkProcessor, EraseSectors, RateComputer, Snapshot, SnapshotRequest, VisualTest
|
||||
from ereuse_devicehub.resources.device import models as m
|
||||
from ereuse_devicehub.resources.device.exceptions import NeedsId
|
||||
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
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures('auth_app_context')
|
||||
def test_snapshot_model():
|
||||
"""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')
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_snapshot_schema(app: Devicehub):
|
||||
with app.app_context():
|
||||
s = file('basic.snapshot')
|
||||
app.resources['Snapshot'].schema.load(s)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_snapshot_post(user: UserClient):
|
||||
"""Tests the post snapshot endpoint (validation, etc), data correctness,
|
||||
and relationship correctness.
|
||||
|
@ -96,6 +98,8 @@ def test_snapshot_post(user: UserClient):
|
|||
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):
|
||||
"""Tests adding and removing components and some don't generate HID.
|
||||
All computers generate HID.
|
||||
|
@ -229,6 +233,8 @@ def _test_snapshot_computer_no_hid(user: UserClient):
|
|||
user.post(s, res=Snapshot)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.xfail(reason='Needs to fix it')
|
||||
def test_snapshot_post_without_hid(user: UserClient):
|
||||
"""Tests the post snapshot endpoint (validation, etc), data correctness,
|
||||
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 'actions' not in snapshot['device']
|
||||
assert 'author' not in snapshot['device']
|
||||
assert snapshot['severity'] == 'Warning'
|
||||
response = user.post(snapshot, res=Snapshot)
|
||||
assert response.status == 201
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_snapshot_mismatch_id():
|
||||
"""Tests uploading a device with an ID from another device."""
|
||||
# Note that this won't happen as in this new version
|
||||
|
@ -258,6 +266,7 @@ def test_snapshot_mismatch_id():
|
|||
pass
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_snapshot_tag_inner_tag(tag_id: str, user: UserClient, app: Devicehub):
|
||||
"""Tests a posting Snapshot with a local tag."""
|
||||
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'
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
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."""
|
||||
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)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
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
|
||||
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)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_snapshot_upload_twice_uuid_error(user: UserClient):
|
||||
pc1 = file('basic.snapshot')
|
||||
user.post(pc1, res=Snapshot)
|
||||
user.post(pc1, res=Snapshot, status=UniqueViolation)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_snapshot_component_containing_components(user: UserClient):
|
||||
"""There is no reason for components to have components and when
|
||||
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)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.xfail(reason='It needs to be fixed.')
|
||||
def test_erase_privacy_standards_endtime_sort(user: UserClient):
|
||||
"""Tests a Snapshot with EraseSectors and the resulting privacy
|
||||
properties.
|
||||
|
@ -401,37 +416,6 @@ def test_test_data_storage(user: UserClient):
|
|||
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):
|
||||
"""Like :class:`ereuse_devicehub.resources.device.models.Device.
|
||||
is_similar()` but adapted for testing.
|
||||
|
@ -497,14 +481,7 @@ def snapshot_and_check(user: UserClient,
|
|||
return snapshot
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason='Not implemented yet, new rate is need it')
|
||||
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.mvp
|
||||
@pytest.mark.xfail(reason='Debug and rewrite it')
|
||||
def test_pc_rating_rate_none(user: UserClient):
|
||||
"""Tests a Snapshot with EraseSectors."""
|
||||
|
@ -513,14 +490,7 @@ def test_pc_rating_rate_none(user: UserClient):
|
|||
snapshot, _ = user.post(res=Snapshot, data=s)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_pc_2(user: UserClient):
|
||||
s = file('laptop-hp_255_g3_notebook-hewlett-packard-cnd52270fw.snapshot')
|
||||
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
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||
def test_create_tag():
|
||||
"""Creates a tag specifying a custom organization."""
|
||||
|
@ -34,6 +35,7 @@ def test_create_tag():
|
|||
assert tag.provider == URL('http://foo.bar')
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||
def test_create_tag_default_org():
|
||||
"""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
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||
def test_create_tag_no_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='/')
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||
def test_create_two_same_tags():
|
||||
"""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()
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_tag_post(app: Devicehub, user: UserClient):
|
||||
"""Checks the POST method of creating a 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()
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_tag_post_etag(user: UserClient):
|
||||
"""Ensures users cannot create eReuse.org tags through POST;
|
||||
only terminal.
|
||||
|
@ -93,12 +99,13 @@ def test_tag_post_etag(user: UserClient):
|
|||
user.post({'id': 'FOO-123456'}, res=Tag)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_tag_get_device_from_tag_endpoint(app: Devicehub, user: UserClient):
|
||||
"""Checks getting a linked device from a tag endpoint"""
|
||||
with app.app_context():
|
||||
# Create a pc with a tag
|
||||
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)
|
||||
db.session.add(pc)
|
||||
db.session.commit()
|
||||
|
@ -106,6 +113,7 @@ def test_tag_get_device_from_tag_endpoint(app: Devicehub, user: UserClient):
|
|||
assert computer['serialNumber'] == 'sn1'
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_tag_get_device_from_tag_endpoint_no_linked(app: Devicehub, user: UserClient):
|
||||
"""As above, but when the tag is not linked."""
|
||||
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)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_tag_get_device_from_tag_endpoint_no_tag(user: UserClient):
|
||||
"""As above, but when there is no tag with such ID."""
|
||||
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):
|
||||
"""As above, but when there are two tags with the same ID, the
|
||||
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)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_tag_create_tags_cli(app: Devicehub, user: UserClient):
|
||||
"""Checks creating tags with the CLI endpoint."""
|
||||
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()
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_tag_create_etags_cli(app: Devicehub, user: UserClient):
|
||||
"""Creates an eTag through the CLI."""
|
||||
# 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')
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_tag_manual_link_search(app: Devicehub, user: UserClient):
|
||||
"""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():
|
||||
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.commit()
|
||||
desktop_id = desktop.id
|
||||
|
@ -189,6 +202,7 @@ def test_tag_manual_link_search(app: Devicehub, user: UserClient):
|
|||
assert i['items']
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||
def test_tag_secondary_workbench_link_find(user: UserClient):
|
||||
"""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
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_tag_create_tags_cli_csv(app: Devicehub, user: UserClient):
|
||||
"""Checks creating tags with the CLI endpoint using a 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)
|
||||
|
||||
|
||||
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
|
||||
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']
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_get_tags_endpoint(user: UserClient, app: Devicehub,
|
||||
requests_mock: requests_mock.mocker.Mocker):
|
||||
"""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
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(app_context.__name__)
|
||||
def test_create_user_method_with_agent(app: Devicehub):
|
||||
"""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
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(app_context.__name__)
|
||||
def test_create_user_email_insensitive():
|
||||
"""Ensures email is case insensitive."""
|
||||
|
@ -53,6 +55,7 @@ def test_create_user_email_insensitive():
|
|||
assert u1.email == 'foo@foo.com'
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.usefixtures(app_context.__name__)
|
||||
def test_hash_password():
|
||||
"""Tests correct password hashing and equaling."""
|
||||
|
@ -61,6 +64,7 @@ def test_hash_password():
|
|||
assert user.password == 'foo'
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_login_success(client: Client, app: Devicehub):
|
||||
"""Tests successfully performing login.
|
||||
This checks that:
|
||||
|
@ -83,6 +87,7 @@ def test_login_success(client: Client, app: Devicehub):
|
|||
assert user['inventories'][0]['id'] == 'test'
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_login_failure(client: Client, app: Devicehub):
|
||||
"""Tests performing wrong login."""
|
||||
# Wrong password
|
||||
|
|
|
@ -7,13 +7,14 @@ import pytest
|
|||
|
||||
from ereuse_devicehub.client import UserClient
|
||||
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.models import Device
|
||||
from ereuse_devicehub.resources.tag.model import Tag
|
||||
from tests.conftest import file
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_workbench_server_condensed(user: UserClient):
|
||||
"""As :def:`.test_workbench_server_phases` but all the actions
|
||||
condensed in only one big ``Snapshot`` file, as described
|
||||
|
@ -36,6 +37,7 @@ def test_workbench_server_condensed(user: UserClient):
|
|||
('BenchmarkProcessorSysbench', 5),
|
||||
('StressTest', 1),
|
||||
('EraseSectors', 6),
|
||||
('EreusePrice', 1),
|
||||
('BenchmarkRamSysbench', 1),
|
||||
('BenchmarkProcessor', 5),
|
||||
('Install', 6),
|
||||
|
@ -43,7 +45,6 @@ def test_workbench_server_condensed(user: UserClient):
|
|||
('BenchmarkDataStorage', 6),
|
||||
('BenchmarkDataStorage', 7),
|
||||
('TestDataStorage', 6),
|
||||
('VisualTest', 1),
|
||||
('RateComputer', 1)
|
||||
}
|
||||
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['rate']['closed']
|
||||
assert device['rate']['severity'] == 'Info'
|
||||
assert device['rate']['rating'] == 0
|
||||
assert device['rate']['rating'] == 1
|
||||
assert device['rate']['type'] == RateComputer.t
|
||||
# TODO JN why haven't same order in actions??
|
||||
assert device['actions'][2]['type'] == VisualTest.t
|
||||
assert device['actions'][2]['appearanceRange'] == 'A'
|
||||
assert device['actions'][2]['functionalityRange'] == 'B'
|
||||
# TODO JN why haven't same order in actions on each execution?
|
||||
assert device['actions'][2]['type'] == BenchmarkProcessor.t or device['actions'][2]['type'] == BenchmarkRamSysbench.t
|
||||
assert device['tags'][0]['id'] == 'tag1'
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
@pytest.mark.xfail(reason='Functionality not yet developed.')
|
||||
def test_workbench_server_phases(user: UserClient):
|
||||
"""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?
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_real_hp_11(user: UserClient):
|
||||
s = file('real-hp.snapshot.11')
|
||||
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
||||
|
@ -160,11 +161,13 @@ def test_real_hp_11(user: UserClient):
|
|||
# todo check rating
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_real_toshiba_11(user: UserClient):
|
||||
s = file('real-toshiba.snapshot.11')
|
||||
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient):
|
||||
"""Checks the values of the device, components,
|
||||
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'
|
||||
# TODO add appearance and functionality Range in device[rate]
|
||||
|
||||
assert rate['processorRange'] == 'VERY_LOW'
|
||||
assert rate['ramRange'] == 'VERY_LOW'
|
||||
assert rate['ratingRange'] == 'VERY_LOW'
|
||||
assert rate['processorRange'] == 'LOW'
|
||||
assert rate['ramRange'] == 'LOW'
|
||||
assert rate['ratingRange'] == 'LOW'
|
||||
assert rate['ram'] == 1.53
|
||||
# TODO add camelCase instead of snake_case
|
||||
assert rate['dataStorage'] == 3.76
|
||||
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']
|
||||
wifi = components[0]
|
||||
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.StressTest.t in action_types
|
||||
assert em.Snapshot.t in action_types
|
||||
assert len(actions) == 7
|
||||
assert len(actions) == 8
|
||||
gpu = components[3]
|
||||
assert gpu['model'] == 'atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller'
|
||||
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.StressTest.t in action_types
|
||||
assert em.Snapshot.t in action_types
|
||||
# todo why?? change action types 3 to 5
|
||||
assert len(action_types) == 5
|
||||
assert len(action_types) == 6
|
||||
sound = components[4]
|
||||
assert sound['model'] == 'nm10/ich7 family high definition audio controller'
|
||||
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.EraseBasic.t in action_types
|
||||
assert em.Snapshot.t in action_types
|
||||
# todo why?? change action types 6 to 8
|
||||
assert len(action_types) == 8
|
||||
assert len(action_types) == 9
|
||||
erase = next(e for e in hdd['actions'] if e['type'] == em.EraseBasic.t)
|
||||
assert erase['endTime']
|
||||
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'
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_real_custom(user: UserClient):
|
||||
s = file('real-custom.snapshot.11')
|
||||
snapshot, _ = user.post(res=em.Snapshot, data=s, status=NeedsId)
|
||||
# todo insert with tag
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_real_hp_quad_core(user: UserClient):
|
||||
s = file('real-hp-quad-core.snapshot.11')
|
||||
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_real_eee_1000h(user: UserClient):
|
||||
s = file('asus-eee-1000h.snapshot.11')
|
||||
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."""
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason='It needs to be fixed.')
|
||||
@pytest.mark.parametrize('file',
|
||||
(pytest.param(f, id=f.name)
|
||||
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)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_workbench_asus_1001pxd_rate_low(user: UserClient):
|
||||
"""Tests an Asus 1001pxd with a low rate."""
|
||||
s = file('asus-1001pxd.snapshot')
|
||||
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
||||
|
||||
|
||||
@pytest.mark.mvp
|
||||
def test_david(user: UserClient):
|
||||
s = file('david.lshw.snapshot')
|
||||
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
||||
|
|
Reference in New Issue