mensa 0.6.17 → 0.6.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mensa
4
+ class Verifier
5
+ # One verified aspect of a table, e.g. sorting a column descending.
6
+ #
7
+ # +section+ groups checks in the report (:loads, :columns, :filters, ...),
8
+ # +subject+ is the column, filter or view it relates to. Warnings (N+1
9
+ # queries, slow queries) don't fail the table unless it's verified strict.
10
+ Check = Struct.new(:section, :subject, :name, :status, :message, :sql, :error) do
11
+ def passed?
12
+ status == :passed
13
+ end
14
+
15
+ def failed?
16
+ status == :failed
17
+ end
18
+
19
+ def skipped?
20
+ status == :skipped
21
+ end
22
+
23
+ def warning?
24
+ status == :warning
25
+ end
26
+
27
+ def label
28
+ [subject, name].compact.join(": ")
29
+ end
30
+ end
31
+
32
+ class Result
33
+ SECTIONS = {
34
+ loads: "Loads",
35
+ columns: "Columns",
36
+ filters: "Filters",
37
+ search: "Search",
38
+ render: "Rendering",
39
+ combined: "Combined",
40
+ exports: "Exports",
41
+ grouping: "Grouping",
42
+ views: "Views"
43
+ }.freeze
44
+
45
+ SYMBOLS = {passed: "✓", failed: "✗", skipped: "-", warning: "!"}.freeze
46
+
47
+ attr_reader :table_class, :checks, :label
48
+
49
+ # +label+ names the context it was verified in, e.g. "user: User#1"
50
+ def initialize(table_class, strict: false, label: nil)
51
+ @table_class = table_class
52
+ @strict = strict
53
+ @label = label
54
+ @checks = []
55
+ end
56
+
57
+ # False for a class that isn't a Mensa::Base subclass, which is skipped
58
+ def mensa_table?
59
+ table_class.is_a?(Class) && table_class < Mensa::Base
60
+ end
61
+
62
+ def strict?
63
+ @strict
64
+ end
65
+
66
+ def success?
67
+ failures.empty? && (!strict? || warnings.empty?)
68
+ end
69
+
70
+ def failures
71
+ checks.select(&:failed?)
72
+ end
73
+
74
+ def passes
75
+ checks.select(&:passed?)
76
+ end
77
+
78
+ def skips
79
+ checks.select(&:skipped?)
80
+ end
81
+
82
+ def warnings
83
+ checks.select(&:warning?)
84
+ end
85
+
86
+ def summary
87
+ "#{passes.size} passed, #{failures.size} failed, #{skips.size} skipped, #{warnings.size} #{"warning".pluralize(warnings.size)}"
88
+ end
89
+
90
+ def title
91
+ [table_class.name || table_class.to_s, ("(#{label})" if label)].compact.join(" ")
92
+ end
93
+
94
+ def to_s
95
+ return "#{title}\n - skipped: #{checks.first&.message}" unless mensa_table?
96
+
97
+ lines = [title]
98
+
99
+ checks.group_by(&:section).each do |section, section_checks|
100
+ lines << " #{SECTIONS.fetch(section, section.to_s.humanize)}"
101
+ section_checks.each do |check|
102
+ lines << " #{SYMBOLS[check.status]} #{check.label}#{" (skipped: #{check.message})" if check.skipped?}"
103
+ next unless check.failed? || check.warning?
104
+
105
+ check.message.to_s.each_line { |line| lines << " #{line.chomp}" }
106
+ lines << " SQL: #{check.sql}" if check.sql.present?
107
+ end
108
+ end
109
+
110
+ lines << ""
111
+ lines << "#{success? ? "OK" : "FAILED"}: #{summary}#{" (strict: warnings fail)" if strict? && warnings.any?}"
112
+ lines.join("\n")
113
+ end
114
+ alias_method :report, :to_s
115
+
116
+ def inspect
117
+ "#<#{self.class.name} #{title} #{summary}>"
118
+ end
119
+ end
120
+ end
121
+ end