tina4ruby 3.12.13 → 3.13.0
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.
- checksums.yaml +4 -4
- data/lib/tina4/auth.rb +14 -8
- data/lib/tina4/test.rb +179 -0
- data/lib/tina4/version.rb +1 -1
- data/lib/tina4.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bf3bb8bf3275f76f25951fdb75de731c304323b64cb2a495e4060ad7fcafdde1
|
|
4
|
+
data.tar.gz: a616d714a2a9beecfeb8d42a5b4124540d2abe07be8d9d2cd83ec5996596eb44
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: be8986d4743a886d442d267add5953bff00a24fe85388b1947e57b1c9fa68447db08d63811b1f13d25cacb64eeea80b8e06fe366ec53961290c3125dbea69a1a
|
|
7
|
+
data.tar.gz: 535158f269ed6964d764e010efb319ac343991d9737a41bad8acbefe6eb8e8ff191694fb662a2df4cec684499c4b708ca924b6d80a7b1cfea306323eb697706b
|
data/lib/tina4/auth.rb
CHANGED
|
@@ -109,19 +109,25 @@ module Tina4
|
|
|
109
109
|
end
|
|
110
110
|
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
# Verify a JWT signature + expiry.
|
|
113
|
+
#
|
|
114
|
+
# 3.13.0: return type changed from `Boolean` to `Hash | nil`. The
|
|
115
|
+
# decoded payload is returned on success, nil on failure. Matches
|
|
116
|
+
# firebase/jwt-ruby and Python's Auth.valid_token in 3.13.0.
|
|
117
|
+
#
|
|
118
|
+
# Legacy `if Tina4::Auth.valid_token(t)` patterns keep working
|
|
119
|
+
# because a non-empty Hash is truthy and nil is falsy.
|
|
120
|
+
def valid_token(token)
|
|
113
121
|
if use_hmac?
|
|
114
|
-
|
|
122
|
+
hmac_decode(token, hmac_secret) # returns Hash payload or nil
|
|
115
123
|
else
|
|
116
124
|
ensure_keys
|
|
117
125
|
require "jwt"
|
|
118
|
-
JWT.decode(token, public_key, true, algorithm: "RS256")
|
|
119
|
-
|
|
126
|
+
decoded = JWT.decode(token, public_key, true, algorithm: "RS256")
|
|
127
|
+
decoded[0] # firebase/jwt-ruby returns [payload, header]
|
|
120
128
|
end
|
|
121
|
-
rescue JWT::ExpiredSignature
|
|
122
|
-
|
|
123
|
-
rescue JWT::DecodeError
|
|
124
|
-
false
|
|
129
|
+
rescue JWT::ExpiredSignature, JWT::DecodeError
|
|
130
|
+
nil
|
|
125
131
|
end
|
|
126
132
|
|
|
127
133
|
def valid_token_detail(token)
|
data/lib/tina4/test.rb
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Tina4 — The Intelligent Native Application 4ramework
|
|
4
|
+
# Copyright 2007 - current Tina4
|
|
5
|
+
# License: MIT https://opensource.org/licenses/MIT
|
|
6
|
+
|
|
7
|
+
module Tina4
|
|
8
|
+
# Tina4 xUnit-style test base class — class-based test suites with HTTP
|
|
9
|
+
# helpers and positional assertions. Zero external dependencies.
|
|
10
|
+
#
|
|
11
|
+
# Documentation chapter 18 has long described:
|
|
12
|
+
#
|
|
13
|
+
# class UserApiTest < Tina4::Test
|
|
14
|
+
# def test_health
|
|
15
|
+
# resp = get("/health")
|
|
16
|
+
# assert_equal_value(resp.status, 200)
|
|
17
|
+
# end
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# Until 3.13.0 this class did not exist — examples crashed with
|
|
21
|
+
# "uninitialized constant Tina4::Test". Ruby parity of the
|
|
22
|
+
# Python `tina4_python.test.Test` and PHP `Tina4\Test` classes.
|
|
23
|
+
#
|
|
24
|
+
# The class has a built-in runner (no Minitest/RSpec required):
|
|
25
|
+
#
|
|
26
|
+
# results = Tina4::Test.run_all # discovers all subclasses
|
|
27
|
+
# # => { passed: 12, failed: 0, errors: 0, details: [...] }
|
|
28
|
+
#
|
|
29
|
+
# Or run a single suite class:
|
|
30
|
+
#
|
|
31
|
+
# UserApiTest.run!
|
|
32
|
+
#
|
|
33
|
+
# HTTP helpers (get/post/put/patch/delete) delegate to TestClient.
|
|
34
|
+
# Positional assertions match the Python (actual, expected, message)
|
|
35
|
+
# shape used throughout the cross-framework docs.
|
|
36
|
+
class Test
|
|
37
|
+
# Class-level registry so run_all can discover every subclass without
|
|
38
|
+
# filesystem scanning.
|
|
39
|
+
@subclasses = []
|
|
40
|
+
|
|
41
|
+
class << self
|
|
42
|
+
attr_reader :subclasses
|
|
43
|
+
|
|
44
|
+
def inherited(subclass)
|
|
45
|
+
super
|
|
46
|
+
Test.subclasses << subclass
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Run every test method (`test_*`) on the calling subclass. Returns
|
|
50
|
+
# a hash with passed/failed/errors counts and per-test details.
|
|
51
|
+
def run!
|
|
52
|
+
instance_methods(false).grep(/\Atest_/).sort.each_with_object(
|
|
53
|
+
{ passed: 0, failed: 0, errors: 0, details: [] }
|
|
54
|
+
) do |method, results|
|
|
55
|
+
suite = new
|
|
56
|
+
begin
|
|
57
|
+
suite.send(:set_up)
|
|
58
|
+
suite.send(method)
|
|
59
|
+
suite.send(:tear_down)
|
|
60
|
+
results[:passed] += 1
|
|
61
|
+
results[:details] << { suite: name, test: method, status: "passed" }
|
|
62
|
+
rescue AssertionError => e
|
|
63
|
+
results[:failed] += 1
|
|
64
|
+
results[:details] << { suite: name, test: method, status: "failed", message: e.message }
|
|
65
|
+
rescue StandardError => e
|
|
66
|
+
results[:errors] += 1
|
|
67
|
+
results[:details] << { suite: name, test: method, status: "error", message: "#{e.class}: #{e.message}" }
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Discover and run every Tina4::Test subclass.
|
|
73
|
+
def run_all(quiet: false)
|
|
74
|
+
results = { passed: 0, failed: 0, errors: 0, details: [] }
|
|
75
|
+
Test.subclasses.each do |klass|
|
|
76
|
+
out = klass.run!
|
|
77
|
+
results[:passed] += out[:passed]
|
|
78
|
+
results[:failed] += out[:failed]
|
|
79
|
+
results[:errors] += out[:errors]
|
|
80
|
+
results[:details].concat(out[:details])
|
|
81
|
+
end
|
|
82
|
+
unless quiet
|
|
83
|
+
puts "Tina4 Test results: #{results[:passed]} passed, #{results[:failed]} failed, #{results[:errors]} errors"
|
|
84
|
+
end
|
|
85
|
+
results
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# ── Lifecycle hooks ──────────────────────────────────────────────
|
|
90
|
+
# snake_case Tina4 idiom; override in subclasses.
|
|
91
|
+
|
|
92
|
+
def set_up
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def tear_down
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# ── HTTP test client (lazy) ───────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
def test_client
|
|
101
|
+
@test_client ||= Tina4::TestClient.new
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def get(path, headers: nil)
|
|
105
|
+
test_client.get(path, headers: headers)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def post(path, json: nil, body: nil, headers: nil)
|
|
109
|
+
test_client.post(path, json: json, body: body, headers: headers)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def put(path, json: nil, body: nil, headers: nil)
|
|
113
|
+
test_client.put(path, json: json, body: body, headers: headers)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def patch(path, json: nil, body: nil, headers: nil)
|
|
117
|
+
test_client.patch(path, json: json, body: body, headers: headers)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def delete(path, headers: nil)
|
|
121
|
+
test_client.delete(path, headers: headers)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# ── Positional assertions — (actual, expected, message) shape ─────
|
|
125
|
+
|
|
126
|
+
def assert_equal_value(actual, expected, message = nil)
|
|
127
|
+
return if actual == expected
|
|
128
|
+
|
|
129
|
+
raise AssertionError, message || "Expected #{expected.inspect}, got #{actual.inspect}"
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def assert_not_equal_value(actual, expected, message = nil)
|
|
133
|
+
return unless actual == expected
|
|
134
|
+
|
|
135
|
+
raise AssertionError, message || "Expected #{actual.inspect} != #{expected.inspect}, but they are equal"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def assert_true(value, message = nil)
|
|
139
|
+
return if value
|
|
140
|
+
|
|
141
|
+
raise AssertionError, message || "Expected truthy, got #{value.inspect}"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def assert_false(value, message = nil)
|
|
145
|
+
return unless value
|
|
146
|
+
|
|
147
|
+
raise AssertionError, message || "Expected falsy, got #{value.inspect}"
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def assert_nil_value(value, message = nil)
|
|
151
|
+
return if value.nil?
|
|
152
|
+
|
|
153
|
+
raise AssertionError, message || "Expected nil, got #{value.inspect}"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def assert_not_nil_value(value, message = nil)
|
|
157
|
+
return unless value.nil?
|
|
158
|
+
|
|
159
|
+
raise AssertionError, message || "Expected non-nil, got nil"
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def assert_raises(expected_class, message = nil)
|
|
163
|
+
raise ArgumentError, "Block required" unless block_given?
|
|
164
|
+
|
|
165
|
+
begin
|
|
166
|
+
yield
|
|
167
|
+
rescue StandardError => e
|
|
168
|
+
return if e.is_a?(expected_class)
|
|
169
|
+
|
|
170
|
+
raise AssertionError,
|
|
171
|
+
message || "Expected #{expected_class}, got #{e.class}: #{e.message}"
|
|
172
|
+
end
|
|
173
|
+
raise AssertionError, message || "Expected #{expected_class} to be raised, but nothing was"
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# AssertionError raised by Tina4::Test assertion helpers on failure.
|
|
178
|
+
class AssertionError < StandardError; end
|
|
179
|
+
end
|
data/lib/tina4/version.rb
CHANGED
data/lib/tina4.rb
CHANGED
|
@@ -54,6 +54,7 @@ require_relative "tina4/response_cache"
|
|
|
54
54
|
require_relative "tina4/html_element"
|
|
55
55
|
require_relative "tina4/error_overlay"
|
|
56
56
|
require_relative "tina4/test_client"
|
|
57
|
+
require_relative "tina4/test"
|
|
57
58
|
require_relative "tina4/docs"
|
|
58
59
|
require_relative "tina4/mcp"
|
|
59
60
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tina4ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.13.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tina4 Team
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-06-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|
|
@@ -402,6 +402,7 @@ files:
|
|
|
402
402
|
- lib/tina4/templates/errors/502.twig
|
|
403
403
|
- lib/tina4/templates/errors/503.twig
|
|
404
404
|
- lib/tina4/templates/errors/base.twig
|
|
405
|
+
- lib/tina4/test.rb
|
|
405
406
|
- lib/tina4/test_client.rb
|
|
406
407
|
- lib/tina4/testing.rb
|
|
407
408
|
- lib/tina4/validator.rb
|