-
Notifications
You must be signed in to change notification settings - Fork 55
/
Odoo.query.pq
236 lines (209 loc) · 13.8 KB
/
Odoo.query.pq
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
section OdooUnitTests;
shared Odoo.UnitTest =
[
// Put any common variables here if you only want them to be evaluated once
url = Odoo.TestServer[url],
db = Odoo.TestServer[db],
root = Odoo.Contents(url, db),
search_read = root{[Key="Functions"]}[Data]{[Function="search_read"]}[Data],
resUser = search_read("res.partner", {}, [limit=10]),
// Fact(<Name of the Test>, <Expected Value>, <Actual Value>)
// <Expected Value> and <Actual Value> can be a literal or let statement
facts =
{
Fact(
"Check that we have more than one entry in our nav table",
try true,
try Table.RowCount(root) > 1
),
Fact(
"Check that we have more than one entry in our 'Tables' nav table",
try true,
try Table.RowCount(root{[Key="Tables"]}[Data]) > 1
),
Fact(
"Check that we can query 'res.partner' through search_read",
try true,
try not Table.IsEmpty(resUser)
),
Fact(
"Check that the schema for 'res.partner' is set",
try true,
try List.Distinct(Table.Column(Type.TableSchema(Value.Type(resUser)), "TypeName")) <> {"Any.Type"}
),
Fact(
"Check that we can navigate to 'res.partner' from the nav table",
try true,
try not Table.IsEmpty(root{[Key="Tables"]}[Data]{[Model="res.users"]}[Data]{[Key="res.users"]}[Data])
),
Fact(
"Check that non 'store' fields aren't queried when no fields are specified",
try true,
try not List.Contains(
Table.ColumnNames(resUser),
"__last_update")
),
Fact(
"Check that 'store' fields can be queried when specified",
try true,
try List.Contains(
Table.ColumnNames(search_read("res.partner", {}, [limit=1, fields={"__last_update"}])),
"__last_update")
)
},
report = Facts.Summarize(facts)
][report];
/// COMMON UNIT TESTING CODE
Fact = (_subject as text, _expected, _actual) as record =>
[ expected = _expected,
safeExpected = if expected[HasError] then "Expected : "& @ValueToText(expected[Error]) else expected[Value],
actual = _actual,
safeActual = if actual[HasError] then "Actual : "& @ValueToText(actual[Error]) else actual[Value],
attempt = try safeExpected = safeActual,
result = if attempt[HasError] or not attempt[Value] then "Failure ⛔" else "Success ✓",
resultOp = if result = "Success ✓" then " = " else " <> ",
addendumEvalAttempt = if attempt[HasError] then @ValueToText(attempt[Error]) else "",
addendumEvalExpected = try @ValueToText(safeExpected) otherwise "...",
addendumEvalActual = try @ValueToText (safeActual) otherwise "...",
fact =
[ Result = result &" "& addendumEvalAttempt,
Notes =_subject,
Details = " ("& addendumEvalExpected & resultOp & addendumEvalActual &")"
]
][fact];
Facts = (_subject as text, _predicates as list) => List.Transform(_predicates, each Fact(_subject,_{0},_{1}));
Facts.Summarize = (_facts as list) as table =>
[ Fact.CountSuccesses = (count, i) =>
[ result = try i[Result],
sum = if result[HasError] or not Text.StartsWith(result[Value], "Success") then count else count + 1
][sum],
passed = List.Accumulate(_facts, 0, Fact.CountSuccesses),
total = List.Count(_facts),
format = if passed = total then "All #{0} Passed !!! ✓" else "#{0} Passed ☺ #{1} Failed ☹",
result = if passed = total then "Success" else "⛔",
rate = Number.IntegerDivide(100*passed, total),
header =
[ Result = result,
Notes = Text.Format(format, {passed, total-passed}),
Details = Text.Format("#{0}% success rate", {rate})
],
report = Table.FromRecords(List.Combine({{header},_facts}))
][report];
ValueToText = (value, optional depth) =>
let
List.TransformAndCombine = (list, transform, separator) => Text.Combine(List.Transform(list, transform), separator),
Serialize.Binary = (x) => "#binary(" & Serialize(Binary.ToList(x)) & ") ",
Serialize.Function = (x) => _serialize_function_param_type(
Type.FunctionParameters(Value.Type(x)),
Type.FunctionRequiredParameters(Value.Type(x)) ) &
" as " &
_serialize_function_return_type(Value.Type(x)) &
" => (...) ",
Serialize.List = (x) => "{" & List.TransformAndCombine(x, Serialize, ", ") & "} ",
Serialize.Record = (x) => "[ " &
List.TransformAndCombine(
Record.FieldNames(x),
(item) => Serialize.Identifier(item) & " = " & Serialize(Record.Field(x, item)),
", ") &
" ] ",
Serialize.Table = (x) => "#table( type " &
_serialize_table_type(Value.Type(x)) &
", " &
Serialize(Table.ToRows(x)) &
") ",
Serialize.Identifier = Expression.Identifier,
Serialize.Type = (x) => "type " & _serialize_typename(x),
_serialize_typename = (x, optional funtype as logical) => /* Optional parameter: Is this being used as part of a function signature? */
let
isFunctionType = (x as type) => try if Type.FunctionReturn(x) is type then true else false otherwise false,
isTableType = (x as type) => try if Type.TableSchema(x) is table then true else false otherwise false,
isRecordType = (x as type) => try if Type.ClosedRecord(x) is type then true else false otherwise false,
isListType = (x as type) => try if Type.ListItem(x) is type then true else false otherwise false
in
if funtype=null and isTableType(x) then _serialize_table_type(x) else
if funtype=null and isListType(x) then "{ " & @_serialize_typename( Type.ListItem(x) ) & " }" else
if funtype=null and isFunctionType(x) then "function " & _serialize_function_type(x) else
if funtype=null and isRecordType(x) then _serialize_record_type(x) else
if x = type any then "any" else
let base = Type.NonNullable(x) in
(if Type.IsNullable(x) then "nullable " else "") &
(if base = type anynonnull then "anynonnull" else
if base = type binary then "binary" else
if base = type date then "date" else
if base = type datetime then "datetime" else
if base = type datetimezone then "datetimezone" else
if base = type duration then "duration" else
if base = type logical then "logical" else
if base = type none then "none" else
if base = type null then "null" else
if base = type number then "number" else
if base = type text then "text" else
if base = type time then "time" else
if base = type type then "type" else
/* Abstract types: */
if base = type function then "function" else
if base = type table then "table" else
if base = type record then "record" else
if base = type list then "list" else
"any /*Actually unknown type*/"),
_serialize_table_type = (x) =>
let
schema = Type.TableSchema(x)
in
"table " &
(if Table.IsEmpty(schema) then "" else
"[" & List.TransformAndCombine(
Table.ToRecords(Table.Sort(schema,"Position")),
each Serialize.Identifier(_[Name]) & " = " & _[Kind],
", ") &
"] "),
_serialize_record_type = (x) =>
let flds = Type.RecordFields(x)
in
if Record.FieldCount(flds)=0 then "record" else
"[" & List.TransformAndCombine(
Record.FieldNames(flds),
(item) => Serialize.Identifier(item) & "=" & _serialize_typename(Record.Field(flds,item)[Type]),
", ") &
(if Type.IsOpenRecord(x) then ", ..." else "") &
"]",
_serialize_function_type = (x) => _serialize_function_param_type(
Type.FunctionParameters(x),
Type.FunctionRequiredParameters(x) ) &
" as " &
_serialize_function_return_type(x),
_serialize_function_param_type = (t,n) =>
let
funsig = Table.ToRecords(
Table.TransformColumns(
Table.AddIndexColumn( Record.ToTable( t ), "isOptional", 1 ),
{ "isOptional", (x)=> x>n } ) )
in
"(" &
List.TransformAndCombine(
funsig,
(item)=>
(if item[isOptional] then "optional " else "") &
Serialize.Identifier(item[Name]) & " as " & _serialize_typename(item[Value], true),
", ") &
")",
_serialize_function_return_type = (x) => _serialize_typename(Type.FunctionReturn(x), true),
Serialize = (x) as text =>
if x is binary then try Serialize.Binary(x) otherwise "null /*serialize failed*/" else
if x is date then try Expression.Constant(x) otherwise "null /*serialize failed*/" else
if x is datetime then try Expression.Constant(x) otherwise "null /*serialize failed*/" else
if x is datetimezone then try Expression.Constant(x) otherwise "null /*serialize failed*/" else
if x is duration then try Expression.Constant(x) otherwise "null /*serialize failed*/" else
if x is function then try Serialize.Function(x) otherwise "null /*serialize failed*/" else
if x is list then try Serialize.List(x) otherwise "null /*serialize failed*/" else
if x is logical then try Expression.Constant(x) otherwise "null /*serialize failed*/" else
if x is null then try Expression.Constant(x) otherwise "null /*serialize failed*/" else
if x is number then try Expression.Constant(x) otherwise "null /*serialize failed*/" else
if x is record then try Serialize.Record(x) otherwise "null /*serialize failed*/" else
if x is table then try Serialize.Table(x) otherwise "null /*serialize failed*/" else
if x is text then try Expression.Constant(x) otherwise "null /*serialize failed*/" else
if x is time then try Expression.Constant(x) otherwise "null /*serialize failed*/" else
if x is type then try Serialize.Type(x) otherwise "null /*serialize failed*/" else
"[#_unable_to_serialize_#]"
in
try Serialize(value) otherwise "<serialization failed>";