Compare commits

...

4 Commits

  1. 5
      .gitignore
  2. 22
      basic/compexec.py
  3. 8
      basic/constants.py
  4. 13
      basic/message.py
  5. 1
      basic/step.py
  6. 7
      basic/text_const.py
  7. 18
      basic/toolHandling.py
  8. 8
      collect_testcase.py
  9. 7
      compare_testcase.py
  10. 8
      declare_result.py
  11. 10
      execute_testcase.py
  12. 1
      generate_testcases.py
  13. 7
      start_dialog.py
  14. 43
      test/test_06file.py
  15. 4
      test/test_07catalog.py
  16. 8
      test/test_12toolhandling.py
  17. 13
      test/test_24gen.py
  18. 197
      test/test_25map.py
  19. 2
      test/test_31db.py
  20. 59
      test/test_32file.py
  21. 7
      test_executer.py
  22. 1
      utils/data_const.py
  23. 20
      utils/file_abstract.py
  24. 30
      utils/file_tool.py
  25. 1
      utils/filejson_tool.py
  26. 5
      utils/filexml_tool.py
  27. 0
      utils/flask_tool.py
  28. 3
      utils/gen_tool.py
  29. 11
      utils/i18n_tool.py
  30. 375
      utils/map_tool.py
  31. 8
      webdev.sh
  32. 57
      webflask/__init__.py
  33. 31
      webflask/application.py
  34. 92
      webflask/auth.py
  35. 31
      webflask/components.py
  36. 31
      webflask/datest.py
  37. 41
      webflask/db.py
  38. 31
      webflask/environment.py
  39. 23
      webflask/index.py
  40. 31
      webflask/reports.py
  41. 17
      webflask/schema.sql
  42. 20
      webflask/static/script.js
  43. 31
      webflask/static/style.css
  44. 9
      webflask/templates/application/overview.html
  45. 16
      webflask/templates/auth/login.html
  46. 17
      webflask/templates/auth/register.html
  47. 40
      webflask/templates/base.html
  48. 9
      webflask/templates/datest/overview.html
  49. 9
      webflask/templates/environment/overview.html
  50. 12
      webflask/templates/index.html
  51. 9
      webflask/templates/reports/overview.html
  52. 32
      webflask/templates/testcase/overview.html
  53. 9
      webflask/templates/testsuite/overview.html
  54. 41
      webflask/testcase.py
  55. 31
      webflask/testsuite.py

5
.gitignore

@ -7,4 +7,9 @@ test/environment
test/conf
.idea
config
instance
doc
venv
lib

22
basic/compexec.py

@ -49,6 +49,8 @@ import utils.match_tool
import utils.match_const as M
import utils.tdata_tool
import basic.constants as B
import basic.text_const as T
import utils.data_const as D
class Testexecuter():
@ -112,7 +114,7 @@ class Testexecuter():
print (t)
if utils.db_abstract.isCompTable(self, tdata, t):
self.m.logInfo("insert content "+ self.name)
dbi = basic.toolHandling.getDbTool(self, job)
dbi = basic.toolHandling.getDbTool(job, self)
dbi.insertTables(tdata, job)
break
self.m.setMsg("data loaded for " + self.name + " is OK")
@ -139,7 +141,7 @@ class Testexecuter():
self.m.debug(verify, "--- " + str(inspect.currentframe().f_code.co_name) + "() started at " + datetime.now().strftime("%Y%m%d_%H%M%S") + " for " + str(self.name).upper())
if B.TOPIC_NODE_DB in self.conf[B.SUBJECT_ARTS]:
self.m.logInfo("select db-content "+ self.name)
dbi = basic.toolHandling.getDbTool(self, job)
dbi = basic.toolHandling.getDbTool(job, self)
data = dbi.selectTables(subdir, job)
print("ppp")
#data = {}
@ -252,16 +254,18 @@ class Testexecuter():
:param tdata:
:return:
"""
if step.fct in self.conf["teststeps"]:
for s in self.conf["teststeps"][step.fct]:
stepconf = self.conf["teststeps"][step.fct][s]
if stepconf["tool"] == "file":
if not step.fct in self.conf[B.DATA_NODE_STEPS]:
raise Exception(self.m.getMessageText(T.EXP_KEY_DOESNT_EXIST, [step.fct, self.name]))
if step.fct in self.conf[B.DATA_NODE_STEPS]:
for stepconf in self.conf[B.DATA_NODE_STEPS][step.fct]:
if stepconf[B.SUBJECT_TOOL] == B.TOPIC_NODE_FILE:
tool = basic.toolHandling.getFileTool(job, self, stepconf[B.ATTR_ARTS_TYPE])
print("file-tool")
elif stepconf["tool"] == "api":
elif stepconf[B.SUBJECT_TOOL] == B.TOPIC_NODE_API:
print("api-tool")
elif stepconf["tool"] == "cli":
elif stepconf[B.SUBJECT_TOOL] == B.TOPIC_NODE_CLI:
print("cli-tool")
elif stepconf["tool"] == "db":
elif stepconf[B.SUBJECT_TOOL] == B.TOPIC_NODE_DB:
print("db-tool")
else:
print("nichts da")

8
basic/constants.py

@ -95,7 +95,9 @@ DATA_NODE_DDL = "ddl"
The fields are defined in data_const (D) """
DATA_NODE_COMP = "comp"
""" This constant defines """
DATA_NODE_PAR = "par"
DATA_NODE_PAR = "par"
DATA_NODE_CATALOG = "_catalog"
DATA_NODE_ROW = "_row"
ATTR_ARTS_TYPE = "type"
@ -173,6 +175,8 @@ ATTR_PATH_EXPECT = "expect"
""" This constant defines the folder in testing-filesystem for test-expectation values """
ATTR_PATH_PROGRAM = "program"
""" This constant defines the program-folder in the workspace """
ATTR_PATH_COMPS = "components"
""" This constant defines the subfolder in the program-folder in the workspace """
ATTR_PATH_ENV = "environment"
""" This constant defines the folder in testing-filesystem, used for configs related to environments """
ATTR_PATH_RELEASE = "release"
@ -217,6 +221,8 @@ ATTR_EXEC_REF = "_exec"
ATTR_DATA_REF = "_nr"
ATTR_DATA_COMP = "_comp"
SUBJECT_TOOL = "tool"
# -------------------------------------------------------------
# exception texts
EXP_NO_BASIS_FILE = "basis file cant be found"

13
basic/message.py

@ -21,6 +21,8 @@ import os
import math
from datetime import datetime
import utils.path_tool
import utils.i18n_tool
import basic.text_const as T
LIMIT_FATAL = 0
LIMIT_ERROR = 4
@ -55,6 +57,7 @@ class Message:
"""
def __init__(self, job, level, logTime, componente):
# (self, componente, out, level):
self.job = job
self.componente = componente # dezantrales Logsystem
verify = LIMIT_DEBUG
self.initErrorTyp()
@ -191,6 +194,16 @@ class Message:
self.setRc(RC_MSG, text)
self.logInfo(text)
def getMessageText(self, text, args):
out = ""
constName = ""
for i in range(0, len(T.LIST_EXP_TEXT)):
if text == T.LIST_EXP_TEXT[i]:
constName = T.LIST_EXP_CONST[i]
txt = utils.i18n_tool.I18n.getInstance().getMessage(self.job, constName, args)
out = txt.format(args)
return out
def logFatal(self, text):
self.log(LIMIT_FATAL, "FATAL: " + text)
self.debug(LIMIT_FATAL, "FATAL: " + text)

1
basic/step.py

@ -73,6 +73,7 @@ def parseStep(job, fields):
step.comp = fields[D.STEP_COMP_I]
step.execStep = fields[D.STEP_EXECNR_I]
step.refLine = fields[D.STEP_REFNR_I]
setattr(step, B.ATTR_DATA_REF, step.refLine)
if D.STEP_ARGS_I == D.STEP_LIST_I:
args = ""
for i in range(D.STEP_ARGS_I, len(fields)):

7
basic/text_const.py

@ -0,0 +1,7 @@
# -----------------
EXP_KEY_MISSING = "key is missing {}"
EXP_KEY_DOESNT_EXIST = "key {} doesnt exist in domain {}"
LIST_EXP_TEXT = [EXP_KEY_MISSING, EXP_KEY_DOESNT_EXIST]
LIST_EXP_CONST = ["EXP_KEY_MISSING", "EXP_KEY_DOESNT_EXIST"]

18
basic/toolHandling.py

@ -50,18 +50,18 @@ def getCompAttr(comp, topic, attr, table=""):
def getTool(technicType, comp, job):
if technicType == B.TOPIC_NODE_DB:
return getDbTool(comp, job)
return getDbTool(job, comp)
if technicType == B.TOPIC_NODE_CLI:
return getCliTool(comp, job)
return getCliTool(job, comp)
if technicType == B.TOPIC_NODE_API:
return getApiTool(comp, job)
return getApiTool(job, comp)
if technicType == B.TOPIC_NODE_FILE:
# TODO im Allgemeinen keine konrete Implementierung aufrufen,
# denn zu einer Komponente koennen unterschiedliche Dateien vorkommen
return getFileTool(job, comp, "")
# class ToolManager:
def getDbTool(comp, job):
def getDbTool(job, comp, dbtype=""):
verify = int(job.getDebugLevel("db_tool"))
dbtype = getCompAttr(comp, B.TOPIC_NODE_DB, B.ATTR_TYPE, "")
toolname = "db"+dbtype+"_tool"
@ -75,7 +75,7 @@ def getDbTool(comp, job):
c.setComp(comp)
return c
def getCliTool(comp, job):
def getCliTool(job, comp):
verify = int(job.getDebugLevel("db_tool"))
clitype = getCompAttr(comp, B.TOPIC_NODE_CLI, B.ATTR_TYPE, "")
toolname = "cli"+clitype+"_tool"
@ -89,7 +89,7 @@ def getCliTool(comp, job):
c.setComp(comp)
return c
def getApiTool(comp, job):
def getApiTool(job, comp):
verify = int(job.getDebugLevel("db_tool"))
apitype = getCompAttr(comp, B.TOPIC_NODE_API, B.ATTR_TYPE, "")
toolname = "api"+apitype+"_tool"
@ -104,9 +104,11 @@ def getApiTool(comp, job):
return c
def getFileTool(job, comp, filenode=""):
verify = int(job.getDebugLevel("db_tool"))
if len(filenode) > 3 and filenode[-1:] != ".":
verify = int(job.getDebugLevel("file_tool"))
if len(filenode) > 3 and "." in filenode and filenode[-1:] != ".":
filetype = utils.config_tool.getAttribute(comp, filenode, B.ATTR_ARTS_TYPE, job)
elif len(filenode) > 2 and len(filenode) < 5:
filetype = filenode
else:
filetype = getCompAttr(comp, B.TOPIC_NODE_FILE, B.ATTR_TYPE, "")
toolname = "file"+filetype+"_tool"

8
collect_testcase.py

@ -1,4 +1,10 @@
# This is a sample Python script.
#!/usr/bin/python
# program to collect results from the system
# PARAM: --environment --application --tcdir [ testcase, tctime ]
# main functions
# + collect_testcase() : system --> data --> archiv.result
# + post_testcase() : comp-config --> system
# ---------------------------------------------------
import sys#
# import jsonpickle # pip install jsonpickle
import yaml # pip install pyyaml

7
compare_testcase.py

@ -1,4 +1,9 @@
# This is a sample Python script.
#!/usr/bin/python
# program to compare the results of a testcase
# PARAM: --environment --application --tcdir [ testcase, tctime ]
# main functions
# + compare_testcase() : archiv.result --> data --> archiv.matchresult
# ---------------------------------------------------
import sys#
import os
import basic.program as program

8
declare_result.py

@ -1,4 +1,10 @@
# This is a sample Python script.
#!/usr/bin/python
# program to declare new exceptions from actual results
# PARAM from INPUT: --testcase/testsuite
# main functions
# + input_param() : cache-actjob --> user-input --> local-param
# + start_job() : local-param --> cache-actjob --> start-param
# ---------------------------------------------------
import sys#
# import jsonpickle # pip install jsonpickle
import yaml # pip install pyyaml

10
execute_testcase.py

@ -1,5 +1,11 @@
# This is a sample Python script.
import sys#
#!/usr/bin/python
# program to execute steps of a testcase
# PARAM: --environment --application --tcdir [ testcase, tctime ]
# main functions
# + craete_request() : testspec --> tdata.step --> archiv.request
# + send_request() : archiv.request -- comp-config --> system.interface
# ---------------------------------------------------
import sys
import os
import basic.step
import basic.program as program

1
generate_testcases.py

@ -1,3 +1,4 @@
# https://ucarmesin.de/index.php/it/testautomatisierung-fuer-daten-test/232-testfallgenerierung
import random
#from flask import
#

7
start_dialog.py

@ -1,3 +1,10 @@
#!/usr/bin/python
# program to execute programs for a testcases or for a testsuite
# PARAM from INPUT: --granularity --application --environment --testcase/testsuite
# main functions
# + input_param() : cache-actjob --> user-input --> local-param
# + start_job() : local-param --> cache-actjob --> start-param
# ---------------------------------------------------
"""

43
test/test_06file.py

@ -4,13 +4,16 @@ import inspect
import utils.file_tool as t
import utils.path_tool
import basic.program
import test.constants
import test.constants as T
import test.testtools
import pprint
import json
HOME_PATH = test.constants.HOME_PATH
DATA_PATH = test.constants.DATA_PATH
TEST_FUNCTIONS = ["test_getFiles", "test_pathTool", "test_encoding"]
TEST_FUNCTIONS = ["test_getFiles", "test_pathTool", "test_encoding", "test_11readYml", "test_14readXml"]
TEST_FUNCTIONS = ["test_11readYml"]
verbose = False
class MyTestCase(unittest.TestCase):
mymsg = ""
@ -64,10 +67,44 @@ class MyTestCase(unittest.TestCase):
cnttest += 3
MyTestCase.mymsg += "\n----- " + actfunction + " : " + str(cnttest)
def test_11readYml(self):
global mymsg
actfunction = str(inspect.currentframe().f_code.co_name)
cnttest = 0
if actfunction not in TEST_FUNCTIONS:
return
job = test.testtools.getJob()
pathname = os.path.join(T.COMP_PATH, "testrest", "mapping-rest.yml")
res = utils.file_tool.readFileDict(pathname, job.m)
print(res)
pathname = os.path.join(DATA_PATH, "tdata", "UNIT_TEST", "rest-message.xml")
utils.file_tool.writeFileDict(job.m, pathname, res)
def test_14readXml(self):
global mymsg
actfunction = str(inspect.currentframe().f_code.co_name)
cnttest = 0
if actfunction not in TEST_FUNCTIONS:
return
job = test.testtools.getJob()
pathname = os.path.join(DATA_PATH, "tdata", "UNIT_TEST", "shiporder.xml")
res = utils.file_tool.readFileDict(pathname, job.m)
res = dict(res)
print(res)
self.assertIn("shiporder", res)
self.assertIn("@orderid", res["shiporder"])
for x in res["shiporder"]:
print(x+" "+str(type(res["shiporder"][x])))
pathname = os.path.join(DATA_PATH, "tdata", "UNIT_TEST", "shiporder-res.yml")
utils.file_tool.writeFileDict(job.m, pathname, res)
MyTestCase.mymsg += "\n----- " + actfunction + " : " + str(cnttest)
def test_zzz(self):
print(MyTestCase.mymsg)
if __name__ == '__main__':
verbose = True
unittest.main()

4
test/test_07catalog.py

@ -15,7 +15,7 @@ OS_SYSTEM = test.constants.OS_SYSTEM
# here you can select single testfunction for developping the tests
TEST_FUNCTIONS = ["test_01class", "test_02read", "test_03key"]
TEST_FUNCTIONS = [ "test_03key"]
#TEST_FUNCTIONS = [ "test_03key"]
verbose = False
class MyTestCase(unittest.TestCase):
@ -70,6 +70,8 @@ class MyTestCase(unittest.TestCase):
self.assertEqual(res["Land"], "Tschad")
print(str(res))
cnttest += 1
res = catalog.getValue("sender", "firma", job)
print(str(res))
MyTestCase.mymsg += "\n----- "+actfunction+" : "+str(cnttest)

8
test/test_12toolhandling.py

@ -48,9 +48,9 @@ class MyTestCase(unittest.TestCase):
comp.conf[B.SUBJECT_CONN][B.TOPIC_NODE_CLI] = {}
comp.conf[B.SUBJECT_CONN][B.TOPIC_NODE_DB][B.ATTR_TYPE] = "mysql"
comp.conf[B.SUBJECT_CONN][B.TOPIC_NODE_CLI][B.ATTR_TYPE] = "ssh"
tool = basic.toolHandling.getDbTool(comp)
tool = basic.toolHandling.getDbTool(job, comp)
self.assertRegex(str(type(tool)), 'dbmysql_tool.DbFcts')
tool = basic.toolHandling.getCliTool(comp)
tool = basic.toolHandling.getCliTool(job, comp)
self.assertRegex(str(type(tool)), 'clissh_tool.CliFcts')
comp.conf[B.SUBJECT_CONN][B.TOPIC_NODE_FILE] = {}
comp.conf[B.SUBJECT_CONN][B.TOPIC_NODE_FILE][B.ATTR_TYPE] = "xml"
@ -59,8 +59,8 @@ class MyTestCase(unittest.TestCase):
comp.conf[B.SUBJECT_CONN][B.TOPIC_NODE_DB][B.ATTR_TYPE] = "dxx"
comp.conf[B.SUBJECT_CONN][B.TOPIC_NODE_CLI][B.ATTR_TYPE] = "sxx"
self.assertRaises(FileNotFoundError, basic.toolHandling.getDbTool, comp)
self.assertRaises(FileNotFoundError, basic.toolHandling.getCliTool, comp)
self.assertRaises(FileNotFoundError, basic.toolHandling.getDbTool, job, comp)
self.assertRaises(FileNotFoundError, basic.toolHandling.getCliTool, job, comp)
if __name__ == '__main__':

13
test/test_24gen.py

@ -33,6 +33,19 @@ class MyTestCase(unittest.TestCase):
print((str(res)))
res = utils.gen_tool.getElemList(G.KEY_LIST, "cat:countries", 6, job)
print((str(res)))
for cnt in [3, 15, 75, 400]:
res = utils.gen_tool.getElemList(G.KEY_LIST, "A, B,C , D", cnt, job)
self.assertEqual(len(res), cnt)
cnttest += 1
for cnt in [3, 15, 75, 400]:
res = utils.gen_tool.getElemList(G.KEY_LIST, "cat:countries", cnt, job)
self.assertEqual(len(res), cnt)
cnttest += 1
for cnt in [3, 15, 75, 400]:
res = utils.gen_tool.getElemList(G.KEY_LIST, "0 .. 4", cnt, job)
self.assertEqual(len(res), cnt)
cnttest += 1
MyTestCase.mymsg += "\n----- "+actfunction+" : "+str(cnttest)

197
test/test_25map.py

@ -0,0 +1,197 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# ---------------------------------------------------------------------------------------------------------
# Author : Ulrich Carmesin
# Source : gitea.ucarmesin.de
# ---------------------------------------------------------------------------------------------------------
import unittest
import inspect
import os
import json
import basic.program
import utils.path_tool
import utils.path_const as P
import utils.config_tool
import utils.data_const as D
import basic.toolHandling
import test.constants
import basic.component
import basic.constants as B
import utils.map_tool
import utils.file_tool
import test.testtools
import utils.tdata_tool
HOME_PATH = test.constants.HOME_PATH
conf = {}
# here you can select single testfunction for developping the tests
# "test_toolhandling", "test_parseSql" -> test of components
TEST_FUNCTIONS = ["test_02getIds", "test_03extractIds", "test_04getFieldVal", "test_05getValue",
"test_11mapTdata"]
TEST_FUNCTIONS = ["test_11mapTdata"]
verbose = False
class MyTestCase(unittest.TestCase):
mymsg = "--------------------------------------------------------------"
def test_03extractIds(self):
global mymsg
actfunction = str(inspect.currentframe().f_code.co_name)
cnttest = 0
if actfunction not in TEST_FUNCTIONS:
return
job = test.testtools.getJob()
res = utils.map_tool.extractIds(job, "1")
self.assertEqual(res, ["1"])
res = utils.map_tool.extractIds(job, "1, 2")
self.assertEqual(res, "1,2".split(","))
res = utils.map_tool.extractIds(job, "1-3")
self.assertEqual(res, "1,2,3".split(","))
res = utils.map_tool.extractIds(job, "1, 3-6, 8")
self.assertEqual(res, "1,3,4,5,6,8".split(","))
cnttest += 4
MyTestCase.mymsg += "\n----- "+actfunction+" : "+str(cnttest)
def test_02getIds(self):
global mymsg
actfunction = str(inspect.currentframe().f_code.co_name)
cnttest = 0
if actfunction not in TEST_FUNCTIONS:
return
job = test.testtools.getJob()
path = utils.config_tool.getConfigPath(P.KEY_TESTCASE, "TC0001", "", job)
tdata = utils.tdata_tool.getCsvSpec(job.m, path, D.CSV_SPECTYPE_DATA)
args = {}
args[B.DATA_NODE_DATA] = tdata
args[B.DATA_NODE_STEPS] = tdata[B.DATA_NODE_STEPS][0]
args[utils.map_tool.ACT_ID] = {}
args[utils.map_tool.ID_LIST] = {}
ids = {}
res = utils.map_tool.getIds(job, args, "msgid={_steps._nr}")
print(res)
self.assertEqual(res[0], "1")
self.assertIn("msgid", args[utils.map_tool.ID_LIST])
cnttest += 1
res = utils.map_tool.getIds(job, args, "sender={_data.person._sender(_steps._nr)}")
print(res)
self.assertEqual(res[0], "firma")
args[B.DATA_NODE_STEPS] = tdata[B.DATA_NODE_STEPS][1]
res = utils.map_tool.getIds(job, args, "msgid={_steps._nr}")
self.assertEqual(res[1], "2")
self.assertIn("msgid", args[utils.map_tool.ID_LIST])
cnttest += 1
args[utils.map_tool.ACT_ID]["msgid"] = "1"
res = utils.map_tool.getIds(job, args, "posid={_data.product._nr,_pos(msgid)}")
self.assertEqual(res[0], ("1", "4"))
self.assertEqual(res[1], ("1", "5"))
cnttest += 1
compName = "testcrmdb"
args[B.DATA_NODE_COMP] = test.testtools.getComp(compName)
comp = args[B.DATA_NODE_COMP]
conf = utils.config_tool.getConfig(D.DDL_FILENAME, compName, "person")
comp.conf[B.DATA_NODE_DDL] = {}
comp.conf[B.DATA_NODE_DDL]["person"] = conf
res = utils.map_tool.getIds(job, args, "fields={_comp.ddl.person}")
print(res)
args[utils.map_tool.ACT_ID]["posid"] = ("1", "4")
res = utils.map_tool.getConditions(job, args, "{posid}")
print(res)
self.assertEqual(res[0], ("1", "4"))
#self.assertEqual(res[1], "4")
cnttest += 1
MyTestCase.mymsg += "\n----- "+actfunction+" : "+str(cnttest)
def test_04getFieldVal(self):
global mymsg
actfunction = str(inspect.currentframe().f_code.co_name)
cnttest = 0
if actfunction not in TEST_FUNCTIONS:
return
job = test.testtools.getJob()
path = utils.config_tool.getConfigPath(P.KEY_TESTCASE, "TC0001", "", job)
tdata = utils.tdata_tool.getCsvSpec(job.m, path, D.CSV_SPECTYPE_DATA)
condIds = [["1"]]
args = {}
args[B.DATA_NODE_DATA] = tdata
res = utils.map_tool.getFieldVal(job, args, "person", "_sender", condIds)
print(res)
condIds = [["1"], ["4"]]
res = utils.map_tool.getFieldVal(job, args, "product", "descript", condIds)
print(res)
def test_05getValue(self):
global mymsg
actfunction = str(inspect.currentframe().f_code.co_name)
cnttest = 0
if actfunction not in TEST_FUNCTIONS:
return
job = test.testtools.getJob()
path = utils.config_tool.getConfigPath(P.KEY_TESTCASE, "TC0001", "", job)
tdata = utils.tdata_tool.getCsvSpec(job.m, path, D.CSV_SPECTYPE_DATA)
condIds = [["1"]]
args = {}
args[B.DATA_NODE_DATA] = tdata
args[B.DATA_NODE_STEPS] = tdata[B.DATA_NODE_STEPS][0]
args[utils.map_tool.ACT_ID] = {}
args[utils.map_tool.ID_LIST] = {}
#res = utils.map_tool.getValue(job, args, "msgid={_steps._nr}")
#-print(res)
#self.assertEqual(res, ['1'])
args[utils.map_tool.ACT_ID]["msgid"] = ['1']
#res = utils.map_tool.getValue(job, args, "sender={_data.person._sender(msgid)}")
#self.assertEqual(res, ["firma"])
#print(res)
print(args[utils.map_tool.ID_LIST])
args[utils.map_tool.ACT_ID]["sender"] = "firma"
res = utils.map_tool.getValue(job, args, "{_catalog.sender.depart(sender)}")
self.assertEqual(res, "main")
res = utils.map_tool.getValue(job, args, "{_steps.args.action}")
print(res)
res = utils.map_tool.getValue(job, args, "{_par.tctime}")
print(res)
args[utils.map_tool.ACT_ID]["msgid"] = "1"
res = utils.map_tool.getValue(job, args, "{_data.person.famname(msgid)}")
print(res)
self.assertEqual(res, "Brecht")
args[utils.map_tool.ACT_ID]["msgid"] = "1"
# select row if field is missing
res = utils.map_tool.getValue(job, args, "{_data.person(msgid)}")
print(res)
res = utils.map_tool.getValue(job, args, "hier ist ein Text")
print(res)
# it is one row [{f_1: v_2, ...}]
def test_11mapTdata(self):
global mymsg
actfunction = str(inspect.currentframe().f_code.co_name)
cnttest = 0
if actfunction not in TEST_FUNCTIONS:
return
job = test.testtools.getJob()
comp = test.testtools.getComp("testrest")
path = os.path.join(job.conf.confs[B.SUBJECT_PATH][B.ATTR_PATH_COMPS], "testrest", "mapping-rest.yml")
mapping = utils.file_tool.readFileDict(path, job.m)
path = utils.config_tool.getConfigPath(P.KEY_TESTCASE, "TC0001", "", job)
tdata = utils.tdata_tool.getCsvSpec(job.m, path, D.CSV_SPECTYPE_DATA)
res = utils.map_tool.mapTdata(job, mapping, tdata, tdata[B.DATA_NODE_STEPS][1], comp)
print(res)
for format in ["xml", "yml", "json"]:
path = os.path.join(job.conf.confs[B.SUBJECT_PATH][B.ATTR_PATH_TDATA], "temp", "result-rest."+format)
print(path)
utils.file_tool.writeFileDict(job.m, path, res)
doc = json.dumps(res, indent=0)
print(doc)
MyTestCase.mymsg += "\n----- "+actfunction+" : "+str(cnttest)
def test_zzz(self):
print(MyTestCase.mymsg)
if __name__ == '__main__':
verbose = True
unittest.main()

2
test/test_31db.py

@ -77,7 +77,7 @@ class MyTestCase(unittest.TestCase):
comp.conf[B.SUBJECT_CONN] = {}
comp.conf[B.SUBJECT_CONN][B.TOPIC_NODE_DB] = {}
comp.conf[B.SUBJECT_CONN][B.TOPIC_NODE_DB][B.ATTR_TYPE] = "shive"
tool = basic.toolHandling.getDbTool(comp)
tool = basic.toolHandling.getDbTool(job, comp)
self.assertRegex(str(type(tool)), 'dbshive_tool.DbFcts')
attr = tool.getDbAttributes("xx")
self.assertRegex(attr[B.ATTR_DB_PARTITION], 'n')

59
test/test_32file.py

@ -0,0 +1,59 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# ---------------------------------------------------------------------------------------------------------
# Author : Ulrich Carmesin
# Source : gitea.ucarmesin.de
# ---------------------------------------------------------------------------------------------------------
import unittest
import inspect
import os
import basic.program
import utils.path_tool
import utils.path_const as P
import utils.config_tool
import utils.data_const as D
import basic.toolHandling
import test.constants
import basic.component
import basic.constants as B
import utils.file_abstract
import utils.file_tool
import test.testtools
import utils.tdata_tool
HOME_PATH = test.constants.HOME_PATH
conf = {}
# here you can select single testfunction for developping the tests
# "test_toolhandling", "test_parseSql" -> test of components
TEST_FUNCTIONS = ["test_11mapTdata"]
TEST_FUNCTIONS = ["test_05getValue"]
verbose = False
class MyTestCase(unittest.TestCase):
mymsg = "--------------------------------------------------------------"
def test_11mapTdata(self):
global mymsg
actfunction = str(inspect.currentframe().f_code.co_name)
cnttest = 0
if actfunction not in TEST_FUNCTIONS:
return
job = test.testtools.getJob()
comp = test.testtools.getComp("testrest")
path = os.path.join(job.conf.confs[B.SUBJECT_PATH][B.ATTR_PATH_COMPS], "testrest", "mapping-rest.yml")
mapping = utils.file_tool.readFileDict(path, job.m)
path = utils.config_tool.getConfigPath(P.KEY_TESTCASE, "TC0001", "", job)
tdata = utils.tdata_tool.getCsvSpec(job.m, path, D.CSV_SPECTYPE_DATA)
res = utils.file_abstract.mapTdata(job, mapping, tdata, tdata[B.DATA_NODE_STEPS][0], comp)
MyTestCase.mymsg += "\n----- "+actfunction+" : "+str(cnttest)
def test_zzz(self):
print(MyTestCase.mymsg)
if __name__ == '__main__':
verbose = True
unittest.main()

7
test_executer.py

@ -1,3 +1,10 @@
#!/usr/bin/python
# program to execute automatically programs for a testsuite
# PARAM: --environment --application --tsdir [ testsuite, tstime ] --step
# main functions
# + execute_step() : testspec --> tdata.steps --> call( init_testsuite/testcase(),
# execute_testcase/testsuite(), collect_testcase/testsuite(), compare_testcase(), finish_testsuite() )
# ---------------------------------------------------
from datetime import datetime
import traceback
import basic.program

1
utils/data_const.py

@ -25,6 +25,7 @@ DDL_TYPE = "type"
DFILE_TYPE_YML = "yml"
DFILE_TYPE_JSON = "json"
DFILE_TYPE_CSV = "csv"
DFILE_TYPE_XML = "xml"
DFILE_TESTCASE_NAME = "testspec"
DFILE_TESTSUITE_NAME = "testsuite"
DFILE_TABLE_PREFIX = "table_"

20
utils/file_abstract.py

@ -5,6 +5,7 @@
# Source : gitea.ucarmesin.de
# ---------------------------------------------------------------------------------------------------------
import os
import re
import basic.program
import basic.catalog
import utils.config_tool
@ -13,16 +14,8 @@ import basic.toolHandling
import utils.data_const as D
import utils.file_tool
import utils.path_tool
"""
# TODO Beschreibung file-tools
* fileXml_tool
* fileJson_tool
* fileYaml_tool
* fileCsv_tool
* fileHtml_tool
...
"""
import xml.etree.ElementTree as ET
import basic.catalog
class FileFcts():
@ -46,7 +39,6 @@ class FileFcts():
:return:
"""
def file2dict(self):
pass
@ -84,7 +76,7 @@ class FileFcts():
#txt = self.createDict()
utils.file_tool.writeFileText(self.comp.m, archivpath, txt)
def send_request(self, step):
def send_request(self, job, step):
archivpath = ""
filename = step.args["filename"]
technique = step.args["technique"]
@ -95,11 +87,11 @@ class FileFcts():
continue
envpath = o["envpath"]
envpath = utils.path_tool.composePattern(envpath, self.comp)
fct = basic.toolHandling.getCliTool(self.comp)
fct = basic.toolHandling.getCliTool(job, self.comp)
fct.copy(self.job, archivpath, envpath)
elif technique == "api":
txt = utils.file_tool.readFileText(archivpath, self.comp.m)
fct = basic.toolHandling.getApiTool(self.comp)
fct = basic.toolHandling.getApiTool(job, self.comp)
response = fct.send(self.job, self.comp, txt)
archivpath = os.path.join(utils.path_tool.composePattern("{tcresult}/response", self.comp), filename)

30
utils/file_tool.py

@ -9,6 +9,7 @@ import os
import os.path
import re
import xmltodict
import yaml
import basic.message
@ -222,10 +223,30 @@ def readFileDict(path, msg):
with open(path, 'r', encoding=enc) as file:
doc = json.load(file)
file.close()
elif D.DFILE_TYPE_XML in path[-4:]:
with open(path, 'r', encoding=enc) as file:
res = xmltodict.parse(file.read())
# doc = dict(res)
doc = castOrderedDict(res)
file.close()
elif D.DFILE_TYPE_CSV in path[-5:]:
doc = utils.tdata_tool.getCsvSpec(msg, path, D.CSV_SPECTYPE_CONF)
return doc
def castOrderedDict(res, job=None, key=""):
if isinstance(res, dict):
doc = dict(res)
for x in doc:
doc[x] = castOrderedDict(doc[x], job, x)
elif isinstance(res, list):
sublist = []
for i in range(0, len(res)):
sublist.append(castOrderedDict(res[i], job, ""))
doc = sublist
else:
doc = res
return doc
def writeFileText(msg, path, text, enc="utf-8"):
job = basic.program.Job.getInstance()
@ -245,7 +266,14 @@ def writeFileDict(msg, path, dict, enc="utf-8"):
file.close()
elif D.DFILE_TYPE_JSON in path[-5:]:
with open(path, 'w', encoding=enc) as file:
doc = json.dumps(file, indent=4)
doc = json.dumps(dict, indent=4)
file.write(doc)
file.close()
elif D.DFILE_TYPE_XML in path[-4:]:
with open(path, 'w', encoding=enc) as file:
text = xmltodict.unparse(dict, pretty=True)
if "<?xml version=" not in text:
text = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + text
file.write(text)
file.close()

1
utils/filejson_tool.py

@ -17,3 +17,4 @@ class FileFcts(utils.file_abstract.FileFcts):
def __init__(self):
pass

5
utils/filexml_tool.py

@ -4,6 +4,7 @@
# Author : Ulrich Carmesin
# Source : gitea.ucarmesin.de
# ---------------------------------------------------------------------------------------------------------
import xmltodict
import basic.program
import utils.config_tool
import utils.file_abstract
@ -17,3 +18,7 @@ class FileFcts(utils.file_abstract.FileFcts):
def __init__(self):
pass
def parseFile2Dict(self):
pass

0
utils/flask_tool.py

3
utils/gen_tool.py

@ -122,9 +122,6 @@ def getElemList(formula, values, count, job):
temp = catalog.getKeys(domain, job)
if not isinstance(temp, list):
temp = []
max = 3
while len(temp) > 0 and len(out) < count:
out += temp
max -= 1
if max < 0: break
return out[0:count]

11
utils/i18n_tool.py

@ -37,6 +37,12 @@ class I18n:
return I18n.__instance
def getMessage(self, job, key, args=[]):
print("getMessage "+key+" "+str(args))
out = self.getText(key, job)
out = out.format(args)
return out
def getText(self, key, job=None):
"""
this function gets the text depending on language which is set in job.conf
@ -52,8 +58,9 @@ class I18n:
language = "en"
if language not in self.cache:
raise Exception(EXP_KEY_MISSING, (key))
out = self.extractText(key)
key = self.extractKey(key)
if "=" in key:
out = self.extractText(key)
key = self.extractKey(key)
if key in self.cache[language]:
out = self.cache[language][key]
elif key in self.cache[DEFAULT_LANGUAGE]:

375
utils/map_tool.py

@ -0,0 +1,375 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# ---------------------------------------------------------------------------------------------------------
# Author : Ulrich Carmesin
# Source : gitea.ucarmesin.de
# ---------------------------------------------------------------------------------------------------------
import os
import re
import basic.program
import basic.catalog
import utils.config_tool
import basic.constants as B
import basic.toolHandling
import utils.data_const as D
import utils.file_tool
import utils.path_tool
import basic.catalog
ACT_ID = "actid"
ID_LIST = "idlist"
MAP_ID = "_id"
MAP_FOR = "_foreach"
MAP_ROW = "_row"
MAP_FCTS = [ MAP_FOR, MAP_ID, MAP_ROW ]
MODUL_NAME = "map_tool"
def mapTdata(job, mapping, tdata, step, comp):
"""
initialize the mapping from testdata into the mapping-structure
:param job:
:param mapping:
:param tdata:
:param step:
:param comp:
:return:
"""
verify = job.getDebugLevel(MODUL_NAME)
out = {}
path = ""
job.debug(verify, mapping)
args = {}
args[B.DATA_NODE_COMP] = comp
args[B.DATA_NODE_DATA] = tdata
args[B.DATA_NODE_STEPS] = step
args[ACT_ID] = {}
args[ID_LIST] = {}
out = mapElement(job, args, mapping, path, out)
job.debug(verify, ">>>>>>>>>>> \n"+str(out))
return out
def mapElement(job, args, elem, path, out):
"""
recursive mapping with building the dict of the testdata
:param job:
:param args:
:param elem:
:param path:
:param out:
:return:
"""
verify = job.getDebugLevel(MODUL_NAME)
job.debug(verify, "mapElem "+path+" id: "+str(args[ACT_ID]))
if isinstance(elem, dict):
job.debug(verify, "##### dict ")
nodes = []
attrNodes = []
leafNodes = []
objNodes = []
for k in elem:
if MAP_ID in elem[k] or MAP_FOR in elem[k]:
objNodes.append(k)
elif k[0:1] == '@' or k[0:1] == '#':
attrNodes.append(k)
else:
leafNodes.append(k)
job.debug(verify, "nodes "+str(attrNodes)+" - "+str(leafNodes)+" - "+str(objNodes))
nodes = attrNodes + leafNodes + objNodes
for k in nodes:
# iterating this elem is declared inside of the elem
# like foreach but only one element
job.debug(verify, "# # "+k)
if MAP_ID in elem[k] or MAP_FOR in elem[k]:
job.debug(verify, "# + k in obj : val "+k)
if MAP_ID in elem[k]:
key = elem[k][MAP_ID][0:elem[k][MAP_ID].find("=")]
idlist = getIds(job, args, elem[k][MAP_ID])
if len(idlist) > 1:
uniqueKeys = {}
for x in idlist:
uniqueKeys[x] = x
if len(uniqueKeys) > 1:
raise Exception("bei keyword _id there is only one element allowed "+str(idlist))
else:
idlist = uniqueKeys.keys()
elif MAP_FOR in elem[k]:
key = elem[k][MAP_FOR][0:elem[k][MAP_FOR].find("=")]
idlist = getIds(job, args, elem[k][MAP_FOR])
sublist = []
a = path.split(",")
a.append(k)
npath = ",".join(a)
for id in idlist:
args[ACT_ID][key] = str(id)
if MAP_ROW in elem[k]:
row = getRow(job, args, elem[k][MAP_ROW])
sublist.append(mapElement(job, args, elem[k], npath, {}))
out[k] = sublist
elif k == MAP_ID or k == MAP_FOR or k == MAP_ROW:
job.debug(verify, "# + k in MAP : continue "+k)
continue
else:
job.debug(verify, "# + k in leaf : val "+k)
a = path.split(",")
a.append(k)
npath = ",".join(a)
job.debug(verify, "mapElem - dict "+k)
out[k] = mapElement(job, args, elem[k], npath, {})
elif isinstance(elem, list):
out = []
i = 0
for k in elem:
job.debug(verify, "mapElem - list "+str(k))
a = path.split(",")
a.append(str(i))
npath = ",".join(a)
out.append(mapElement(job, args, elem[i], path, {}))
i += 1
else:
job.debug(verify, "mapElem - leaf " + elem)
if elem[0:1] == "{" and elem[-1:] == "}":
elem = elem[1:-1]
out = toSimpleType(job, getValue(job, args, elem))
return out
def toSimpleType(job, value):
if isinstance(value, (list, tuple)) and len(value) == 1:
return value[0]
return value
def extractIds(job, idval):
ids = []
if isinstance(idval, list) or isinstance(idval, tuple):
a = idval
else:
a = idval.split(",")
for k in a:
if "-" in k:
b = k.split("-")
for i in range(int(b[0].strip()), int(b[1].strip())+1):
ids.append(str(i).strip())
elif isinstance(k, str):
ids.append(k.strip())
else:
ids.append(k)
return ids
def getRow(job, args, fieldkey):
a = fieldkey.split(".")
row = getValue(job, args, fieldkey)
if B.DATA_NODE_ROW not in args: args[B.DATA_NODE_ROW] = {}
a[1] = a[1][0:a[1].find("(")]
args[B.DATA_NODE_ROW][a[1]] = row[0]
return row
def getIds(job, args, fieldkey):
"""
sets the id resp list of ids into args[idlist]
the fieldkey has a formula like id={_source.table.field(condition)}
:param job:
:param args:
:param fieldky:
:return:
"""
verify = job.getDebugLevel(MODUL_NAME)
job.debug(verify, "match = "+fieldkey)
out = []
idfield = fieldkey
if re.match(r"(.+)\=(.+)", fieldkey):
res = re.search(r"(.+)\=(.+)", fieldkey)
idfield = res.group(1)
fieldkey = res.group(2)
if fieldkey[0:1] == "{" and fieldkey[-1:] == "}":
fieldkey = fieldkey[1:-1]
if "temp" not in args: args["temp"] = {}
i = 0
while "(" in fieldkey and ")" in fieldkey:
innerCond = fieldkey[fieldkey.rfind("(")+1:fieldkey.find(")")]
if "." not in innerCond:
break
innerkey = "temp_"+str(i)
args["temp"][innerkey] = {}
args["temp"][innerkey]["idfield"] = idfield
args["temp"][innerkey]["fieldkey"] = fieldkey
innerlist = getIds(job, args, innerkey+"={"+innerCond+"}")
args[ACT_ID][innerkey] = ",".join(innerlist)
fieldkey = fieldkey.replace(innerCond, innerkey)
idfield = args["temp"][innerkey]["idfield"]
i += 1
if i > 3:
raise Exception("too much searches "+str(args["temp"]))
val = getValue(job, args, fieldkey)
idlist = extractIds(job, val)
args[ID_LIST][idfield] = idlist
job.debug(verify, "idlist " + str(args[ID_LIST]))
return idlist
def getConditions(job, args, fieldkey):
"""
gets a list of condition-value
:param job:
:param args:
:param fieldkey: in formula (c_1, c_2, ..)
:return: [v_1} ..]
"""
verify = job.getDebugLevel(MODUL_NAME)
while fieldkey[0:1] == "(" and fieldkey[-1:] == ")":
fieldkey = fieldkey[1:-1]
while fieldkey[0:1] == "{" and fieldkey[-1:] == "}":
fieldkey = fieldkey[1:-1]
out = []
a = fieldkey.split(",")
if len(a) > 1:
job.m.logError("map-condition should not have more parts - use tupel "+fieldkey)
for k in a:
if "." in k:
raise Exception("Formatfehler in " + fieldkey)
job.debug(verify, "actid " + str(args[ACT_ID]))
idelem = {}
idelem[k] = args[ACT_ID][k]
out.append(args[ACT_ID][k])
return out
def getValue(job, args, fieldkey):
"""
gets the value of the formula like {_source.table.field(condition)}
:param job:
:param args:
:param fieldkey:
:return:
"""
verify = job.getDebugLevel(MODUL_NAME)
job.debug(verify, "getValue "+fieldkey)
while fieldkey[0:1] == "{" and fieldkey[-1:] == "}":
fieldkey = fieldkey[1:-1]
val = ""
idfield = ""
source = ""
table = ""
field = ""
cond = ""
condIds = []
# special cases of id-management
# args[actid][xid] = actual id with name xid
# args[idlist][xid] = list of all ids with name xid
# a) declaration of the id : id={fielddefinition}
if re.match(r".+\=.+", fieldkey):
job.debug(verify, "getValue 222 " + fieldkey)
raise Exception("getIds sollte an passender Stelle direkt aufgerufen werden "+fieldkey)
return getIds(job, args, fieldkey)
# b) set of defined ids - neither fielddefinition nor a function
#elif "." not in fieldkey and "(" not in fieldkey or re.match(r"\(.+\)", fieldkey):
#print("getValue 226 " + fieldkey)
#raise Exception("getConditions sollte an passender Stelle direkt aufgerufen werden")
#return getConditions(job, args, fieldkey)
# fielddefinition with .-separated parts
b = fieldkey.split(".")
job.debug(verify, "match field "+fieldkey)
if re.match(r"(_.+)\..+\..+\(.+\)", fieldkey):
res = re.match(r"(_.+)\.(.+)\.(.+\(.+\))", fieldkey)
job.debug(verify, "match mit ()")
source = res.group(1)
table = res.group(2)
field = res.group(3)
#cond = res.group(4)
#condIds = getValue(job, args, cond)
elif len(b) == 1:
field = b[0]
elif len(b) == 2:
source = b[0]
field = b[1]
elif len(b) == 3:
source = b[0]
table = b[1]
field = b[2]
if re.match(r".+\(.+\)", field):
res = re.match(r"(.+)\((.+)\)", field)
field = res.group(1)
cond = res.group(2)
condIds = getConditions(job, args, cond)
job.debug(verify, source + " - " + table + " - " + field + " - " + cond + " : " + str(condIds))
if source == B.DATA_NODE_ROW:
if table not in args[B.DATA_NODE_ROW]:
raise Exception("row not initialiazed for table "+table+" "+str(args[B.DATA_NODE_ROW]))
row = args[B.DATA_NODE_ROW][table]
val = row[field]
elif source == B.DATA_NODE_DATA:
job.debug(verify, "data " + b[1])
if len(b) == 3:
job.debug(verify, table + ", " + field + ", " + cond + ", " + str(condIds))
val = toSimpleType(job, getFieldVal(job, args, table, field, condIds))
elif len(b) == 2:
job.debug(verify, table + ", " + field + ", " + cond + ", " + str(condIds))
val = getTdataRow(job, args[B.DATA_NODE_DATA], field, condIds)
elif source == B.DATA_NODE_STEPS:
job.debug(verify, "steps " + table+" - "+ field)
if hasattr(args[B.DATA_NODE_STEPS], field):
val = getattr(args[B.DATA_NODE_STEPS], field)
elif hasattr(args[B.DATA_NODE_STEPS], table):
row = getattr(args[B.DATA_NODE_STEPS], table)
if field in row:
val = row[field]
elif source[1:] == B.DATA_NODE_PAR:
job.debug(verify, "par " + b[1])
if getattr(job.par, b[1]):
val = getattr(job.par, b[1])
elif source == B.DATA_NODE_CATALOG:
job.debug(verify, "catalog " + table+", "+ field)
if len(b) != 3:
job.debug(verify, "catalog-Fehler")
return "Fehler-145"
row = basic.catalog.Catalog.getInstance().getValue(table, args[ACT_ID][cond], job)
if isinstance(row, dict) and field in row:
val = row[field]
elif source[1:] == B.DATA_NODE_COMP:
job.debug(verify, "comp "+table+", "+field)
comp = args[B.DATA_NODE_COMP]
if table == B.DATA_NODE_DDL:
fields = comp.conf[B.DATA_NODE_DDL][field][B.DATA_NODE_HEADER]
val = ",".join(fields)
else:
val = fieldkey
job.debug(verify, "return val "+str(val))
return val
def getFieldVal(job, args, table, field, condIds):
out = []
fields = field.split(",")
for row in getTdataRow(job, args[B.DATA_NODE_DATA], table, condIds):
if len(fields) == 1 and field in row:
out.append( str(row[field]).strip())
else:
tup = tuple()
for f in fields:
if f in row:
t = tuple( str(row[f]).strip() )
tup = tup + t
else:
raise Exception("field is missing in row "+f+", "+str(row))
out.append(tup)
return out
def getTdataRow(job, tdata, table, condIds):
verify = job.getDebugLevel(MODUL_NAME)
out = []
idFields = {}
job.debug(verify, "getTdata "+str(condIds))
for i in range(0, len(condIds)):
idFields[tdata[B.DATA_NODE_TABLES][table][B.DATA_NODE_HEADER][i]] = condIds[i]
job.debug(verify, "idFields "+str(idFields))
for row in tdata[B.DATA_NODE_TABLES][table][B.DATA_NODE_DATA]:
i = 0
for k in idFields:
for j in range(0, len(idFields[k])):
if row[k] == idFields[k][j]:
i += 1
if i == len(idFields):
out.append(row)
return out

8
webdev.sh

@ -0,0 +1,8 @@
#!/usr/bin/bash
# script to start development web in webflask - port 5000
export FLASK_APP=webflask
export FLASK_DEBUG=1
# flask init-db
flask run

57
webflask/__init__.py

@ -0,0 +1,57 @@
import os
from flask import Flask
def create_app(test_config=None):
# create and configure the app
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY='dev',
DATABASE=os.path.join(app.instance_path, 'flaskr.sqlite'),
)
if test_config is None:
# load the instance config, if it exists, when not testing
app.config.from_pyfile('config.py', silent=True)
else:
# load the test config if passed in
app.config.from_mapping(test_config)
# ensure the instance folder exists
try:
os.makedirs(app.instance_path)
except OSError:
pass
from . import db
db.init_app(app)
from . import auth
from . import index
from . import testcase
from . import testsuite
from . import components
from . import reports
from . import environment
app.register_blueprint(auth.bp)
app.register_blueprint(index.bp)
app.register_blueprint(testcase.bp)
app.register_blueprint(testsuite.bp)
app.register_blueprint(components.bp)
app.register_blueprint(reports.bp)
app.register_blueprint(environment.bp)
# a simple page that says hello
@app.route('/hello')
def hello():
return 'Hello, World!'
return app
@app.route('/')
def index():
return 'Index Page'
@app.route('/index')
def index():
return 'Index Page 2'

31
webflask/application.py

@ -0,0 +1,31 @@
# https://flask.palletsprojects.com/en/2.0.x/tutorial/views/
# --------------------------------------------------------------
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from webflask.db import get_db
bp = Blueprint('testcase', __name__, url_prefix='/testcase')
@bp.route('/selection', methods=('GET', 'POST'))
def selection():
if request.method == 'POST':
error = None
flash(error)
return render_template('testcase/selection.html')
@bp.route('/overview', methods=('GET', 'POST'))
def overview():
if request.method == 'POST':
error = None
flash(error)
return render_template('testcase/overview.html')

92
webflask/auth.py

@ -0,0 +1,92 @@
# https://flask.palletsprojects.com/en/2.0.x/tutorial/views/
# --------------------------------------------------------------
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from webflask.db import get_db
bp = Blueprint('auth', __name__, url_prefix='/auth')
@bp.route('/login', methods=('GET', 'POST'))
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
db = get_db()
error = None
user = db.execute(
'SELECT * FROM user WHERE username = ?', (username,)
).fetchone()
if user is None:
error = 'Incorrect username.'
elif not check_password_hash(user['password'], password):
error = 'Incorrect password.'
if error is None:
session.clear()
session['user_id'] = user['id']
return redirect(url_for('index'))
flash(error)
return render_template('auth/login.html')
@bp.route('/register', methods=('GET', 'POST'))
def register():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
db = get_db()
error = None
if not username:
error = 'Username is required.'
elif not password:
error = 'Password is required.'
if error is None:
try:
db.execute(
"INSERT INTO user (username, password) VALUES (?, ?)",
(username, generate_password_hash(password)),
)
db.commit()
except db.IntegrityError:
error = f"User {username} is already registered."
else:
return redirect(url_for("auth.login"))
flash(error)
return render_template('auth/register.html')
@bp.before_app_request
def load_logged_in_user():
user_id = session.get('user_id')
if user_id is None:
g.user = None
else:
g.user = get_db().execute(
'SELECT * FROM user WHERE id = ?', (user_id,)
).fetchone()
@bp.route('/logout')
def logout():
session.clear()
return redirect(url_for('index'))
def login_required(view):
@functools.wraps(view)
def wrapped_view(**kwargs):
if g.user is None:
return redirect(url_for('auth.login'))
return view(**kwargs)
return wrapped_view

31
webflask/components.py

@ -0,0 +1,31 @@
# https://flask.palletsprojects.com/en/2.0.x/tutorial/views/
# --------------------------------------------------------------
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from webflask.db import get_db
bp = Blueprint('components', __name__, url_prefix='/components')
@bp.route('/selection', methods=('GET', 'POST'))
def selection():
if request.method == 'POST':
error = None
flash(error)
return render_template('components/selection.html')
@bp.route('/overview', methods=('GET', 'POST'))
def overview():
if request.method == 'POST':
error = None
flash(error)
return render_template('components/overview.html')

31
webflask/datest.py

@ -0,0 +1,31 @@
# https://flask.palletsprojects.com/en/2.0.x/tutorial/views/
# --------------------------------------------------------------
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from webflask.db import get_db
bp = Blueprint('testcase', __name__, url_prefix='/testcase')
@bp.route('/selection', methods=('GET', 'POST'))
def selection():
if request.method == 'POST':
error = None
flash(error)
return render_template('testcase/selection.html')
@bp.route('/overview', methods=('GET', 'POST'))
def overview():
if request.method == 'POST':
error = None
flash(error)
return render_template('testcase/overview.html')

41
webflask/db.py

@ -0,0 +1,41 @@
# https://flask.palletsprojects.com/en/2.0.x/tutorial/database/
# ----------------------------------------------------------------------------
import sqlite3
import click
from flask import current_app, g
from flask.cli import with_appcontext
def init_db():
db = get_db()
with current_app.open_resource('schema.sql') as f:
db.executescript(f.read().decode('utf8'))
def get_db():
if 'db' not in g:
g.db = sqlite3.connect(
current_app.config['DATABASE'],
detect_types=sqlite3.PARSE_DECLTYPES
)
g.db.row_factory = sqlite3.Row
return g.db
def close_db(e=None):
db = g.pop('db', None)
if db is not None:
db.close()
@click.command('init-db')
@with_appcontext
def init_db_command():
"""Clear the existing data and create new tables."""
init_db()
click.echo('Initialized the database.')
def init_app(app):
app.teardown_appcontext(close_db)
app.cli.add_command(init_db_command)

31
webflask/environment.py

@ -0,0 +1,31 @@
# https://flask.palletsprojects.com/en/2.0.x/tutorial/views/
# --------------------------------------------------------------
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from webflask.db import get_db
bp = Blueprint('environment', __name__, url_prefix='/environment')
@bp.route('/selection', methods=('GET', 'POST'))
def selection():
if request.method == 'POST':
error = None
flash(error)
return render_template('environment/selection.html')
@bp.route('/overview', methods=('GET', 'POST'))
def overview():
if request.method == 'POST':
error = None
flash(error)
return render_template('environment/overview.html')

23
webflask/index.py

@ -0,0 +1,23 @@
# https://flask.palletsprojects.com/en/2.0.x/tutorial/views/
# --------------------------------------------------------------
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from webflask.db import get_db
bp = Blueprint('index', __name__, url_prefix='/')
@bp.route('/', methods=('GET', 'POST'))
def index():
if request.method == 'POST':
error = None
flash(error)
return render_template('index.html')

31
webflask/reports.py

@ -0,0 +1,31 @@
# https://flask.palletsprojects.com/en/2.0.x/tutorial/views/
# --------------------------------------------------------------
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from webflask.db import get_db
bp = Blueprint('reports', __name__, url_prefix='/reports')
@bp.route('/selection', methods=('GET', 'POST'))
def selection():
if request.method == 'POST':
error = None
flash(error)
return render_template('reports/selection.html')
@bp.route('/overview', methods=('GET', 'POST'))
def overview():
if request.method == 'POST':
error = None
flash(error)
return render_template('reports/overview.html')

17
webflask/schema.sql

@ -0,0 +1,17 @@
DROP TABLE IF EXISTS user;
DROP TABLE IF EXISTS post;
CREATE TABLE user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL
);
CREATE TABLE post (
id INTEGER PRIMARY KEY AUTOINCREMENT,
author_id INTEGER NOT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
title TEXT NOT NULL,
body TEXT NOT NULL,
FOREIGN KEY (author_id) REFERENCES user (id)
);

20
webflask/static/script.js

@ -0,0 +1,20 @@
addEventListener("load", addCollaps);
function addCollaps() {
var coll = document.getElementsByClassName("collapsible");
for (var c of coll) {
c.addEventListener("click", function() {
this.nextElementSibling.classList.toggle("collapsed");
});
}
}
// TODO : kann raus
function loadJson(filename, callback) {
var xhr = XMLHttpRequest();
xhr.onreadystatechange = function() {
if(this.readyState == 4 && this.status == 200) {
callback(JSON.parse(xhr.responseText));
}
}
xhr.open(get, filename);
xhr.send();
}

31
webflask/static/style.css

@ -0,0 +1,31 @@
html { font-family: sans-serif; background: #eee; padding: 1rem; }
body { max-width: 960px; margin: 0 auto; background: white; }
h1 { font-family: serif; color: #377ba8; margin: 1rem 0; }
a { color: #377ba8; }
hr { border: none; border-top: 1px solid lightgray; }
nav { background: lightgray; display: flex; align-items: center; padding: 0 0.5rem; }
nav h1 { flex: auto; margin: 0; }
nav h1 a { text-decoration: none; padding: 0.25rem 0.5rem; }
nav ul { display: flex; list-style: none; margin: 0; padding: 0; }
nav ul li a, nav ul li span, header .action { display: block; padding: 0.5rem; }
.content { padding: 0 1rem 1rem; }
.content > header { border-bottom: 1px solid lightgray; display: flex; align-items: flex-end; }
.content > header h1 { flex: auto; margin: 1rem 0 0.25rem 0; }
.flash { margin: 1em 0; padding: 1em; background: #cae6f6; border: 1px solid #377ba8; }
.post > header { display: flex; align-items: flex-end; font-size: 0.85em; }
.post > header > div:first-of-type { flex: auto; }
.post > header h1 { font-size: 1.5em; margin-bottom: 0; }
.post .about { color: slategray; font-style: italic; }
.post .body { white-space: pre-line; }
.content:last-child { margin-bottom: 0; }
.content form { margin: 1em 0; display: flex; flex-direction: column; }
.content label { font-weight: bold; margin-bottom: 0.5em; }
.content input, .content textarea { margin-bottom: 1em; }
.content textarea { min-height: 12em; resize: vertical; }
input.danger { color: #cc2f2e; }
input[type=submit] { align-self: start; min-width: 10em; }
.collapsible { background-color: #777; color: white; cursor: pointer; padding: 5px; width: 100%;
border: none; text-align: left; outline: none; font-size: 15px; }
.collapsed { padding: 0 18px; display: none; overflow: hidden; }
.active, .collapsible:hover { background-color: #ccc; }

9
webflask/templates/application/overview.html

@ -0,0 +1,9 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Overview{% endblock %}</h1>
{% endblock %}
{% block content %}
<p>Hier kommt Inhalt rein</p>
{% endblock %}

16
webflask/templates/auth/login.html

@ -0,0 +1,16 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Log In{% endblock %}</h1>
{% endblock %}
{% block content %}
<form method="post">
<label for="username">Username</label>
<input name="username" id="username" required>
<label for="password">Password</label>
<input type="password" name="password" id="password" required>
<input type="submit" value="Log In">
</form>
<p>hiermit sollte ein Workspace geoeffnet werden <br/> - bestehend aus eigenem testdata-repo und components-repo.</p>
{% endblock %}

17
webflask/templates/auth/register.html

@ -0,0 +1,17 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Register{% endblock %}</h1>
{% endblock %}
{% block content %}
<form method="post">
<label for="username">Username</label>
<input name="username" id="username" required>
<label for="password">Password</label>
<input type="password" name="password" id="password" required>
<input type="submit" value="Register">
</form>
<p>Durch das Registrieren sollte ein Workspace angelegt werden <br/>
- bestehend aus eine testdata-repo und einem components-repo </p>
{% endblock %}

40
webflask/templates/base.html

@ -0,0 +1,40 @@
<!doctype html>
<head>
<title>{% block title %}{% endblock %} - Datest</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<script type="text/javascript" src="{{ url_for('static', filename='script.js') }}"></script>
<script>
var jsonData = "{{ get_json() }}";
</script>
</head>
<body>
<nav>
<h1>Datest</h1>
<ul>
{% if g.user %}
<li><span>{{ g.user['username'] }}</span>
<li><a href="{{ url_for('testcase.overview') }}">Testcase</a>
<li><a href="{{ url_for('testsuite.overview') }}">Testsuite</a>
<li><a href="{{ url_for('reports.overview') }}">Reports</a>
<li><a href="{{ url_for('components.overview') }}">Components</a>
<li><a href="{{ url_for('auth.logout') }}">Log Out</a>
{% else %}
<li><a href="{{ url_for('testcase.overview') }}">Testcase</a>
<li><a href="{{ url_for('testsuite.overview') }}">Testsuite</a>
<li><a href="{{ url_for('reports.overview') }}">Reports</a>
<li><a href="{{ url_for('components.overview') }}">Components</a>
<li><a href="{{ url_for('auth.register') }}">Register</a>
<li><a href="{{ url_for('auth.login') }}">Log In</a>
{% endif %}
</ul>
</nav>
<section class="content">
<header>
{% block header %}{% endblock %}
</header>
{% for message in get_flashed_messages() %}
<div class="flash">{{ message }}</div>
{% endfor %}
{% block content %}{% endblock %}
</section>
</body>

9
webflask/templates/datest/overview.html

@ -0,0 +1,9 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Overview{% endblock %}</h1>
{% endblock %}
{% block content %}
<p>Hier kommt Inhalt rein</p>
{% endblock %}

9
webflask/templates/environment/overview.html

@ -0,0 +1,9 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Overview{% endblock %}</h1>
{% endblock %}
{% block content %}
<p>Hier kommt Inhalt rein</p>
{% endblock %}

12
webflask/templates/index.html

@ -0,0 +1,12 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Overview{% endblock %}</h1>
{% endblock %}
{% block content %}
<p>Hier kommt Inhalt rein:<br/>
- info Datest allgemein -> ucarmesin.de/joomla
- Bereiche Testfaelle (anzahl)
- letzte Updates (git log) </p>
{% endblock %}

9
webflask/templates/reports/overview.html

@ -0,0 +1,9 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Overview{% endblock %}</h1>
{% endblock %}
{% block content %}
<p>Hier kommt Inhalt rein</p>
{% endblock %}

32
webflask/templates/testcase/overview.html

@ -0,0 +1,32 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Overview{% endblock %}</h1>
{% endblock %}
{% block content %}
<p>Hier kommt Inhalt rein. <br/>
Bei angemeldetem User kann auf das eigene Repo schreibend zugegriffen werden,
als Gast nur lesend auf die Vorlage.</p>
<p>Statistische Daten zu dem Testfaellen: <br/>
- Anzahl in Projekten und Anwendungen <br/>
- letzte Durchfuehrungen (log) <br/>
- letzte Aenderungen (git log) <br/>
- info zu Begriff Testfall -> ucarmesin.de/joomla <br/>
- Suchfeld fuer speziellen Testfall -> Anzeige des Testfalls </p>
<button type="button" class="collapsible" data-toggle="collapse">Block Head/Option</button>
<div class="content collapsed" id="collapseOptions">
<p>Diese Attribute ordnen den Testfall in einen Kontext ein und setzen Bearbeitungsparameter fuer die Durchfuehrung.</p>
</div>
<button type="button" class="collapsible" data-toggle="collapse">Block Steps</button>
<div class="content collapsed" id="collapseSteps">
<p>Diese Attribute beschreiben die Schritte, die waehrend der Durchfuehrungsphase ausgefuehrt werden.
Die benoetigten Testdaten werden aus den Tabellen referenziert.</p>
</div>
<button type="collapscontent" class="collapsible" data-toggle="collapse">Block Tabellen</button>
<div class="content collapsed" id="collapseTables">
<p>In den Tabellen sind die Testdaten hinterlegt, die waehrend der Initialisierungsphase vorbefuellt werden
oder waehrend der Durchfuehrungsphase eingespielt werden.</p>
</div>
{% endblock %}

9
webflask/templates/testsuite/overview.html

@ -0,0 +1,9 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Overview{% endblock %}</h1>
{% endblock %}
{% block content %}
<p>Hier kommt Inhalt rein</p>
{% endblock %}

41
webflask/testcase.py

@ -0,0 +1,41 @@
# https://flask.palletsprojects.com/en/2.0.x/tutorial/views/
# --------------------------------------------------------------
import functools
import utils.config_tool
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from webflask.db import get_db
bp = Blueprint('testcase', __name__, url_prefix='/testcase')
@bp.route('/selection', methods=('GET', 'POST'))
def selection():
if request.method == 'POST':
error = None
flash(error)
return render_template('testcase/selection.html')
@bp.route('/overview', methods=('GET', 'POST'))
def overview():
"""
it shows an overview to testcase
:param: nothing
:return: rendered page with jsondata
* info with link to ucarmesin.de/joomla
* last git-updates (specifications)
* last executions (-> reports) with status running|to-check|with-faults|ok
* selection of conrete testcase by application, testsuite, project, regex?, full name
"""
if request.method == 'POST':
error = None
flash(error)
return render_template('testcase/overview.html')

31
webflask/testsuite.py

@ -0,0 +1,31 @@
# https://flask.palletsprojects.com/en/2.0.x/tutorial/views/
# --------------------------------------------------------------
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from webflask.db import get_db
bp = Blueprint('testsuite', __name__, url_prefix='/testsuite')
@bp.route('/selection', methods=('GET', 'POST'))
def selection():
if request.method == 'POST':
error = None
flash(error)
return render_template('testsuite/selection.html')
@bp.route('/overview', methods=('GET', 'POST'))
def overview():
if request.method == 'POST':
error = None
flash(error)
return render_template('testsuite/overview.html')
Loading…
Cancel
Save