panda-core 0.7.1 → 0.7.3
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/panda/core/engine/admin_controller_config.rb +22 -0
- data/lib/panda/core/engine/autoload_config.rb +25 -0
- data/lib/panda/core/engine/generator_config.rb +20 -0
- data/lib/panda/core/engine/importmap_config.rb +31 -0
- data/lib/panda/core/engine/inflections_config.rb +23 -0
- data/lib/panda/core/engine/middleware_config.rb +33 -0
- data/lib/panda/core/engine/omniauth_config.rb +52 -0
- data/lib/panda/core/engine/phlex_config.rb +26 -0
- data/lib/panda/core/engine/test_config.rb +20 -0
- data/lib/panda/core/engine.rb +20 -115
- data/lib/panda/core/shared/generator_config.rb +22 -0
- data/lib/panda/core/shared/inflections_config.rb +24 -0
- data/lib/panda/core/testing/rails_helper.rb +3 -4
- data/lib/panda/core/testing/support/authentication_helpers.rb +58 -0
- data/lib/panda/core/testing/support/authentication_test_helpers.rb +260 -0
- data/lib/panda/core/testing/support/generator_spec_helper.rb +97 -0
- data/lib/panda/core/testing/support/html_helpers.rb +11 -0
- data/lib/panda/core/testing/support/omniauth_setup.rb +17 -0
- data/lib/panda/core/testing/support/service_stubs.rb +40 -0
- data/lib/panda/core/testing/support/setup.rb +11 -0
- data/lib/panda/core/version.rb +1 -1
- data/lib/panda/core.rb +2 -0
- metadata +19 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2d1c4f83377c0b031d5a68ef550668e6ce9d0e09f9d0865da9ab7a5b7349d981
|
|
4
|
+
data.tar.gz: c82ab418857a1b1961270f417f69ae9e236e1bb36fb3612b7c6e7001ac440cc9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3ab5424862f54e04222d4a8909487e0179e5a73ba1852cce491bd0767cfd26d177d02e720317c610d610275bd895f757cec61d337708a211fd12240db6d3fe47
|
|
7
|
+
data.tar.gz: d24d95c56a5ceac68c0713e51cb029a63b27f88476bf1d93007ae6822a472ea456d919e833c0f26817489dee98af8407a3d6f958b8f2d73846a8b5a064e51dd1
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Panda
|
|
4
|
+
module Core
|
|
5
|
+
class Engine < ::Rails::Engine
|
|
6
|
+
# AdminController alias configuration
|
|
7
|
+
module AdminControllerConfig
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
# Create AdminController alias after controllers are loaded
|
|
12
|
+
# This allows other gems to inherit from Panda::Core::AdminController
|
|
13
|
+
initializer "panda_core.admin_controller_alias", after: :load_config_initializers do
|
|
14
|
+
ActiveSupport.on_load(:action_controller_base) do
|
|
15
|
+
Panda::Core.const_set(:AdminController, Panda::Core::Admin::BaseController) unless Panda::Core.const_defined?(:AdminController)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Panda
|
|
4
|
+
module Core
|
|
5
|
+
class Engine < ::Rails::Engine
|
|
6
|
+
# Autoload paths configuration
|
|
7
|
+
module AutoloadConfig
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
config.eager_load_namespaces << Panda::Core::Engine
|
|
12
|
+
|
|
13
|
+
# Add engine's app directories to autoload paths
|
|
14
|
+
# Note: Only add the root directories, not nested subdirectories
|
|
15
|
+
# Zeitwerk will automatically discover nested modules from these roots
|
|
16
|
+
config.autoload_paths += Dir[root.join("app", "models")]
|
|
17
|
+
config.autoload_paths += Dir[root.join("app", "controllers")]
|
|
18
|
+
config.autoload_paths += Dir[root.join("app", "builders")]
|
|
19
|
+
config.autoload_paths += Dir[root.join("app", "components")]
|
|
20
|
+
config.autoload_paths += Dir[root.join("app", "services")]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Panda
|
|
4
|
+
module Core
|
|
5
|
+
class Engine < ::Rails::Engine
|
|
6
|
+
# Generator configuration
|
|
7
|
+
module GeneratorConfig
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
config.generators do |g|
|
|
12
|
+
g.test_framework :rspec
|
|
13
|
+
g.fixture_replacement :factory_bot
|
|
14
|
+
g.factory_bot dir: "spec/factories"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Panda
|
|
4
|
+
module Core
|
|
5
|
+
class Engine < ::Rails::Engine
|
|
6
|
+
# Importmap configuration
|
|
7
|
+
module ImportmapConfig
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
# Add importmap paths from the engine
|
|
12
|
+
initializer "panda_core.importmap", before: "importmap" do |app|
|
|
13
|
+
if app.config.respond_to?(:importmap)
|
|
14
|
+
# Create a new array if frozen
|
|
15
|
+
app.config.importmap.paths = app.config.importmap.paths.dup if app.config.importmap.paths.frozen?
|
|
16
|
+
|
|
17
|
+
# Add our paths
|
|
18
|
+
app.config.importmap.paths << root.join("config/importmap.rb")
|
|
19
|
+
|
|
20
|
+
# Handle cache sweepers similarly
|
|
21
|
+
if app.config.importmap.cache_sweepers.frozen?
|
|
22
|
+
app.config.importmap.cache_sweepers = app.config.importmap.cache_sweepers.dup
|
|
23
|
+
end
|
|
24
|
+
app.config.importmap.cache_sweepers << root.join("app/javascript")
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Panda
|
|
4
|
+
module Core
|
|
5
|
+
class Engine < ::Rails::Engine
|
|
6
|
+
# Inflections configuration
|
|
7
|
+
module InflectionsConfig
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
# Load inflections early to ensure proper constant resolution
|
|
12
|
+
initializer "panda_core.inflections", before: :load_config_initializers do
|
|
13
|
+
ActiveSupport::Inflector.inflections(:en) do |inflect|
|
|
14
|
+
inflect.acronym "CMS"
|
|
15
|
+
inflect.acronym "SEO"
|
|
16
|
+
inflect.acronym "AI"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Panda
|
|
4
|
+
module Core
|
|
5
|
+
class Engine < ::Rails::Engine
|
|
6
|
+
# Middleware configuration for static assets
|
|
7
|
+
module MiddlewareConfig
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
# Make files in public available to the main app (e.g. /panda-core-assets/panda-logo.png)
|
|
12
|
+
config.middleware.use Rack::Static,
|
|
13
|
+
urls: ["/panda-core-assets"],
|
|
14
|
+
root: Panda::Core::Engine.root.join("public"),
|
|
15
|
+
header_rules: [
|
|
16
|
+
# Disable caching in development for instant CSS updates
|
|
17
|
+
[:all, {"Cache-Control" => Rails.env.development? ? "no-cache, no-store, must-revalidate" : "public, max-age=31536000"}]
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
# Make JavaScript files available for importmap
|
|
21
|
+
# Serve from app/javascript with proper MIME types
|
|
22
|
+
config.middleware.use Rack::Static,
|
|
23
|
+
urls: ["/panda", "/panda/core"],
|
|
24
|
+
root: Panda::Core::Engine.root.join("app/javascript"),
|
|
25
|
+
header_rules: [
|
|
26
|
+
[:all, {"Cache-Control" => Rails.env.development? ? "no-cache, no-store, must-revalidate" : "public, max-age=31536000",
|
|
27
|
+
"Content-Type" => "text/javascript; charset=utf-8"}]
|
|
28
|
+
]
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Panda
|
|
4
|
+
module Core
|
|
5
|
+
class Engine < ::Rails::Engine
|
|
6
|
+
# OmniAuth configuration
|
|
7
|
+
module OmniauthConfig
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
initializer "panda_core.omniauth" do |app|
|
|
12
|
+
# Load OAuth provider gems
|
|
13
|
+
require_relative "../oauth_providers"
|
|
14
|
+
Panda::Core::OAuthProviders.setup
|
|
15
|
+
|
|
16
|
+
# Mount OmniAuth at configurable admin path
|
|
17
|
+
app.middleware.use OmniAuth::Builder do
|
|
18
|
+
# Configure OmniAuth to use the configured admin path
|
|
19
|
+
configure do |config|
|
|
20
|
+
config.path_prefix = "#{Panda::Core.config.admin_path}/auth"
|
|
21
|
+
# POST-only for CSRF protection (CVE-2015-9284)
|
|
22
|
+
# All login forms use POST via form_tag method: "post"
|
|
23
|
+
config.allowed_request_methods = [:post]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
Panda::Core.config.authentication_providers.each do |provider_name, settings|
|
|
27
|
+
# Build provider options, allowing custom path name override
|
|
28
|
+
provider_options = settings[:options] || {}
|
|
29
|
+
|
|
30
|
+
# If path_name is specified, use it to override the default strategy name in URLs
|
|
31
|
+
if settings[:path_name].present?
|
|
32
|
+
provider_options = provider_options.merge(name: settings[:path_name])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
case provider_name.to_s
|
|
36
|
+
when "microsoft_graph"
|
|
37
|
+
provider :microsoft_graph, settings[:client_id], settings[:client_secret], provider_options
|
|
38
|
+
when "google_oauth2"
|
|
39
|
+
provider :google_oauth2, settings[:client_id], settings[:client_secret], provider_options
|
|
40
|
+
when "github"
|
|
41
|
+
provider :github, settings[:client_id], settings[:client_secret], provider_options
|
|
42
|
+
when "developer"
|
|
43
|
+
provider :developer if Rails.env.development?
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Panda
|
|
4
|
+
module Core
|
|
5
|
+
class Engine < ::Rails::Engine
|
|
6
|
+
# Phlex configuration
|
|
7
|
+
module PhlexConfig
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
# Load Phlex base component after Rails application is initialized
|
|
12
|
+
# This ensures Rails.application.routes is available
|
|
13
|
+
initializer "panda_core.phlex_base", after: :load_config_initializers do
|
|
14
|
+
require "phlex"
|
|
15
|
+
require "phlex-rails"
|
|
16
|
+
require "literal"
|
|
17
|
+
require "tailwind_merge"
|
|
18
|
+
|
|
19
|
+
# Load the base component
|
|
20
|
+
require root.join("app/components/panda/core/base")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Panda
|
|
4
|
+
module Core
|
|
5
|
+
class Engine < ::Rails::Engine
|
|
6
|
+
# Test environment configuration
|
|
7
|
+
module TestConfig
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
# For testing: Don't expose engine migrations since we use "copy to host app" strategy
|
|
12
|
+
# In test environment, migrations should be copied to the host app
|
|
13
|
+
if Rails.env.test?
|
|
14
|
+
config.paths["db/migrate"] = []
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/panda/core/engine.rb
CHANGED
|
@@ -19,131 +19,36 @@ ensure
|
|
|
19
19
|
$stderr = original_stderr
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
# Load engine configuration modules
|
|
23
|
+
require_relative "engine/test_config"
|
|
24
|
+
require_relative "engine/autoload_config"
|
|
25
|
+
require_relative "engine/middleware_config"
|
|
26
|
+
require_relative "engine/importmap_config"
|
|
27
|
+
require_relative "engine/omniauth_config"
|
|
28
|
+
require_relative "engine/phlex_config"
|
|
29
|
+
require_relative "engine/admin_controller_config"
|
|
30
|
+
|
|
22
31
|
module Panda
|
|
23
32
|
module Core
|
|
24
33
|
class Engine < ::Rails::Engine
|
|
25
34
|
isolate_namespace Panda::Core
|
|
26
35
|
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
config.paths["db/migrate"] = []
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
config.eager_load_namespaces << Panda::Core::Engine
|
|
36
|
+
# Include shared configuration modules
|
|
37
|
+
include Shared::InflectionsConfig
|
|
38
|
+
include Shared::GeneratorConfig
|
|
34
39
|
|
|
35
|
-
#
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
# Make files in public available to the main app (e.g. /panda-core-assets/panda-logo.png)
|
|
45
|
-
config.middleware.use Rack::Static,
|
|
46
|
-
urls: ["/panda-core-assets"],
|
|
47
|
-
root: Panda::Core::Engine.root.join("public"),
|
|
48
|
-
header_rules: [
|
|
49
|
-
# Disable caching in development for instant CSS updates
|
|
50
|
-
[:all, {"Cache-Control" => Rails.env.development? ? "no-cache, no-store, must-revalidate" : "public, max-age=31536000"}]
|
|
51
|
-
]
|
|
52
|
-
|
|
53
|
-
# Make JavaScript files available for importmap
|
|
54
|
-
# Serve from app/javascript with proper MIME types
|
|
55
|
-
config.middleware.use Rack::Static,
|
|
56
|
-
urls: ["/panda", "/panda/core"],
|
|
57
|
-
root: Panda::Core::Engine.root.join("app/javascript"),
|
|
58
|
-
header_rules: [
|
|
59
|
-
[:all, {"Cache-Control" => Rails.env.development? ? "no-cache, no-store, must-revalidate" : "public, max-age=31536000",
|
|
60
|
-
"Content-Type" => "text/javascript; charset=utf-8"}]
|
|
61
|
-
]
|
|
62
|
-
|
|
63
|
-
config.generators do |g|
|
|
64
|
-
g.test_framework :rspec
|
|
65
|
-
g.fixture_replacement :factory_bot
|
|
66
|
-
g.factory_bot dir: "spec/factories"
|
|
67
|
-
end
|
|
40
|
+
# Include engine-specific configuration modules
|
|
41
|
+
include TestConfig
|
|
42
|
+
include AutoloadConfig
|
|
43
|
+
include MiddlewareConfig
|
|
44
|
+
include ImportmapConfig
|
|
45
|
+
include OmniauthConfig
|
|
46
|
+
include PhlexConfig
|
|
47
|
+
include AdminControllerConfig
|
|
68
48
|
|
|
69
49
|
initializer "panda_core.config" do |app|
|
|
70
50
|
# Configuration is already initialized with defaults in Configuration class
|
|
71
51
|
end
|
|
72
|
-
|
|
73
|
-
# Add importmap paths from the engine
|
|
74
|
-
initializer "panda_core.importmap", before: "importmap" do |app|
|
|
75
|
-
if app.config.respond_to?(:importmap)
|
|
76
|
-
# Create a new array if frozen
|
|
77
|
-
app.config.importmap.paths = app.config.importmap.paths.dup if app.config.importmap.paths.frozen?
|
|
78
|
-
|
|
79
|
-
# Add our paths
|
|
80
|
-
app.config.importmap.paths << root.join("config/importmap.rb")
|
|
81
|
-
|
|
82
|
-
# Handle cache sweepers similarly
|
|
83
|
-
if app.config.importmap.cache_sweepers.frozen?
|
|
84
|
-
app.config.importmap.cache_sweepers = app.config.importmap.cache_sweepers.dup
|
|
85
|
-
end
|
|
86
|
-
app.config.importmap.cache_sweepers << root.join("app/javascript")
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
initializer "panda_core.omniauth" do |app|
|
|
91
|
-
# Load OAuth provider gems
|
|
92
|
-
require_relative "oauth_providers"
|
|
93
|
-
Panda::Core::OAuthProviders.setup
|
|
94
|
-
|
|
95
|
-
# Mount OmniAuth at configurable admin path
|
|
96
|
-
app.middleware.use OmniAuth::Builder do
|
|
97
|
-
# Configure OmniAuth to use the configured admin path
|
|
98
|
-
configure do |config|
|
|
99
|
-
config.path_prefix = "#{Panda::Core.config.admin_path}/auth"
|
|
100
|
-
# POST-only for CSRF protection (CVE-2015-9284)
|
|
101
|
-
# All login forms use POST via form_tag method: "post"
|
|
102
|
-
config.allowed_request_methods = [:post]
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
Panda::Core.config.authentication_providers.each do |provider_name, settings|
|
|
106
|
-
# Build provider options, allowing custom path name override
|
|
107
|
-
provider_options = settings[:options] || {}
|
|
108
|
-
|
|
109
|
-
# If path_name is specified, use it to override the default strategy name in URLs
|
|
110
|
-
if settings[:path_name].present?
|
|
111
|
-
provider_options = provider_options.merge(name: settings[:path_name])
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
case provider_name.to_s
|
|
115
|
-
when "microsoft_graph"
|
|
116
|
-
provider :microsoft_graph, settings[:client_id], settings[:client_secret], provider_options
|
|
117
|
-
when "google_oauth2"
|
|
118
|
-
provider :google_oauth2, settings[:client_id], settings[:client_secret], provider_options
|
|
119
|
-
when "github"
|
|
120
|
-
provider :github, settings[:client_id], settings[:client_secret], provider_options
|
|
121
|
-
when "developer"
|
|
122
|
-
provider :developer if Rails.env.development?
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
# Load Phlex base component after Rails application is initialized
|
|
129
|
-
# This ensures Rails.application.routes is available
|
|
130
|
-
initializer "panda_core.phlex_base", after: :load_config_initializers do
|
|
131
|
-
require "phlex"
|
|
132
|
-
require "phlex-rails"
|
|
133
|
-
require "literal"
|
|
134
|
-
require "tailwind_merge"
|
|
135
|
-
|
|
136
|
-
# Load the base component
|
|
137
|
-
require root.join("app/components/panda/core/base")
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
# Create AdminController alias after controllers are loaded
|
|
141
|
-
# This allows other gems to inherit from Panda::Core::AdminController
|
|
142
|
-
initializer "panda_core.admin_controller_alias", after: :load_config_initializers do
|
|
143
|
-
ActiveSupport.on_load(:action_controller_base) do
|
|
144
|
-
Panda::Core.const_set(:AdminController, Panda::Core::Admin::BaseController) unless Panda::Core.const_defined?(:AdminController)
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
52
|
end
|
|
148
53
|
end
|
|
149
54
|
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Panda
|
|
4
|
+
module Core
|
|
5
|
+
module Shared
|
|
6
|
+
# Shared generator configuration for all panda gems
|
|
7
|
+
# This ensures consistent generator behavior across the ecosystem
|
|
8
|
+
module GeneratorConfig
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
included do
|
|
12
|
+
config.generators do |g|
|
|
13
|
+
g.orm :active_record, primary_key_type: :uuid
|
|
14
|
+
g.test_framework :rspec, fixture: true
|
|
15
|
+
g.fixture_replacement nil
|
|
16
|
+
g.view_specs false
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Panda
|
|
4
|
+
module Core
|
|
5
|
+
module Shared
|
|
6
|
+
# Shared inflections configuration for all panda gems
|
|
7
|
+
# Ensures consistent constant naming across the ecosystem
|
|
8
|
+
module InflectionsConfig
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
included do
|
|
12
|
+
# Load inflections early to ensure proper constant resolution
|
|
13
|
+
initializer "panda.inflections", before: :load_config_initializers do
|
|
14
|
+
ActiveSupport::Inflector.inflections(:en) do |inflect|
|
|
15
|
+
inflect.acronym "CMS"
|
|
16
|
+
inflect.acronym "SEO"
|
|
17
|
+
inflect.acronym "AI"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -29,10 +29,9 @@ Shoulda::Matchers.configure do |config|
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
# Load all support files from panda-core
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
end
|
|
32
|
+
# Files are now in lib/panda/core/testing/support/ to be included in the published gem
|
|
33
|
+
support_path = File.expand_path("../support", __FILE__)
|
|
34
|
+
Dir[File.join(support_path, "**/*.rb")].sort.each { |f| require f }
|
|
36
35
|
|
|
37
36
|
RSpec.configure do |config|
|
|
38
37
|
# Include panda-core route helpers by default
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Panda
|
|
4
|
+
module Core
|
|
5
|
+
module AuthenticationHelpers
|
|
6
|
+
# Create test users with fixed IDs for consistent fixture references
|
|
7
|
+
def create_admin_user
|
|
8
|
+
Panda::Core::User.find_or_create_by!(id: "8f481fcb-d9c8-55d7-ba17-5ea5d9ed8b7a") do |user|
|
|
9
|
+
user.email = "admin@test.example.com"
|
|
10
|
+
user.firstname = "Admin"
|
|
11
|
+
user.lastname = "User"
|
|
12
|
+
user.admin = true
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def create_regular_user
|
|
17
|
+
Panda::Core::User.find_or_create_by!(id: "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d") do |user|
|
|
18
|
+
user.email = "user@test.example.com"
|
|
19
|
+
user.firstname = "Regular"
|
|
20
|
+
user.lastname = "User"
|
|
21
|
+
user.admin = false
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# For request specs - set session directly
|
|
26
|
+
def sign_in_as(user)
|
|
27
|
+
session[:user_id] = user.id
|
|
28
|
+
Panda::Core::Current.user = user
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# For system specs - use test session endpoint if available
|
|
32
|
+
def login_as_admin
|
|
33
|
+
admin_user = create_admin_user
|
|
34
|
+
if defined?(Panda::CMS)
|
|
35
|
+
# CMS provides test session endpoint
|
|
36
|
+
post "/admin/test_sessions", params: {user_id: admin_user.id}
|
|
37
|
+
else
|
|
38
|
+
# Fall back to direct session setting
|
|
39
|
+
sign_in_as(admin_user)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def login_as_regular_user
|
|
44
|
+
regular_user = create_regular_user
|
|
45
|
+
if defined?(Panda::CMS)
|
|
46
|
+
post "/admin/test_sessions", params: {user_id: regular_user.id}
|
|
47
|
+
else
|
|
48
|
+
sign_in_as(regular_user)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
RSpec.configure do |config|
|
|
56
|
+
# Include authentication helpers for all spec types (model, request, system, component, etc.)
|
|
57
|
+
config.include Panda::Core::AuthenticationHelpers
|
|
58
|
+
end
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Comprehensive authentication test helpers for Panda Core and consuming gems
|
|
4
|
+
# This module provides helpers for:
|
|
5
|
+
# - Creating test users with fixed IDs
|
|
6
|
+
# - OAuth mocking and configuration
|
|
7
|
+
# - Test session management
|
|
8
|
+
# - Request and system test authentication
|
|
9
|
+
|
|
10
|
+
module Panda
|
|
11
|
+
module Core
|
|
12
|
+
module AuthenticationTestHelpers
|
|
13
|
+
# ============================================================================
|
|
14
|
+
# USER CREATION HELPERS
|
|
15
|
+
# ============================================================================
|
|
16
|
+
|
|
17
|
+
# Create an admin user with fixed ID for consistent fixture references
|
|
18
|
+
def create_admin_user(attributes = {})
|
|
19
|
+
ensure_columns_loaded
|
|
20
|
+
admin_id = "8f481fcb-d9c8-55d7-ba17-5ea5d9ed8b7a"
|
|
21
|
+
Panda::Core::User.find_or_create_by!(id: admin_id) do |user|
|
|
22
|
+
user.email = attributes[:email] || "admin@example.com"
|
|
23
|
+
user.firstname = attributes[:firstname] || "Admin" if user.respond_to?(:firstname=)
|
|
24
|
+
user.lastname = attributes[:lastname] || "User" if user.respond_to?(:lastname=)
|
|
25
|
+
user.name = attributes[:name] || "Admin User" if user.respond_to?(:name=) && !user.respond_to?(:firstname=)
|
|
26
|
+
user.image_url = attributes[:image_url] || default_image_url if user.respond_to?(:image_url=)
|
|
27
|
+
# Use is_admin for the actual column, but support both for compatibility
|
|
28
|
+
if user.respond_to?(:is_admin=)
|
|
29
|
+
user.is_admin = attributes.fetch(:admin, true)
|
|
30
|
+
elsif user.respond_to?(:admin=)
|
|
31
|
+
user.admin = attributes.fetch(:admin, true)
|
|
32
|
+
end
|
|
33
|
+
# Only set OAuth fields if they exist on the model
|
|
34
|
+
user.uid = attributes[:uid] || "admin_oauth_uid_123" if user.respond_to?(:uid=)
|
|
35
|
+
user.provider = attributes[:provider] || "google_oauth2" if user.respond_to?(:provider=)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Create a regular user with fixed ID for consistent fixture references
|
|
40
|
+
def create_regular_user(attributes = {})
|
|
41
|
+
ensure_columns_loaded
|
|
42
|
+
regular_id = "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d"
|
|
43
|
+
Panda::Core::User.find_or_create_by!(id: regular_id) do |user|
|
|
44
|
+
user.email = attributes[:email] || "user@example.com"
|
|
45
|
+
user.firstname = attributes[:firstname] || "Regular" if user.respond_to?(:firstname=)
|
|
46
|
+
user.lastname = attributes[:lastname] || "User" if user.respond_to?(:lastname=)
|
|
47
|
+
user.name = attributes[:name] || "Regular User" if user.respond_to?(:name=) && !user.respond_to?(:firstname=)
|
|
48
|
+
user.image_url = attributes[:image_url] || default_image_url(dark: true) if user.respond_to?(:image_url=)
|
|
49
|
+
# Use is_admin for the actual column, but support both for compatibility
|
|
50
|
+
if user.respond_to?(:is_admin=)
|
|
51
|
+
user.is_admin = attributes.fetch(:admin, false)
|
|
52
|
+
elsif user.respond_to?(:admin=)
|
|
53
|
+
user.admin = attributes.fetch(:admin, false)
|
|
54
|
+
end
|
|
55
|
+
# Only set OAuth fields if they exist on the model
|
|
56
|
+
user.uid = attributes[:uid] || "user_oauth_uid_456" if user.respond_to?(:uid=)
|
|
57
|
+
user.provider = attributes[:provider] || "google_oauth2" if user.respond_to?(:provider=)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Backwards compatibility with fixture access patterns
|
|
62
|
+
def admin_user
|
|
63
|
+
ensure_columns_loaded
|
|
64
|
+
@admin_user ||= Panda::Core::User.find_by(email: "admin@example.com") || create_admin_user
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def regular_user
|
|
68
|
+
ensure_columns_loaded
|
|
69
|
+
@regular_user ||= Panda::Core::User.find_by(email: "user@example.com") || create_regular_user
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# ============================================================================
|
|
73
|
+
# OMNIAUTH HELPERS
|
|
74
|
+
# ============================================================================
|
|
75
|
+
|
|
76
|
+
def clear_omniauth_config
|
|
77
|
+
OmniAuth.config.mock_auth.clear
|
|
78
|
+
Rails.application.env_config.delete("omniauth.auth") if defined?(Rails.application)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def mock_oauth_for_user(user, provider: :google_oauth2)
|
|
82
|
+
clear_omniauth_config
|
|
83
|
+
OmniAuth.config.test_mode = true
|
|
84
|
+
OmniAuth.config.mock_auth[provider] = OmniAuth::AuthHash.new({
|
|
85
|
+
provider: provider.to_s,
|
|
86
|
+
uid: (user.respond_to?(:uid) ? user.uid : nil) || user.id,
|
|
87
|
+
info: {
|
|
88
|
+
email: user.email,
|
|
89
|
+
name: user.name,
|
|
90
|
+
image: user.respond_to?(:image_url) ? user.image_url : nil
|
|
91
|
+
},
|
|
92
|
+
credentials: {
|
|
93
|
+
token: "mock_token_#{user.id}",
|
|
94
|
+
expires_at: Time.now + 1.week
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# ============================================================================
|
|
100
|
+
# REQUEST SPEC HELPERS (Direct Session Manipulation)
|
|
101
|
+
# ============================================================================
|
|
102
|
+
|
|
103
|
+
# For request specs - set session directly (fast, no HTTP requests)
|
|
104
|
+
def sign_in_as(user)
|
|
105
|
+
# This works in request specs where we have direct access to the session
|
|
106
|
+
begin
|
|
107
|
+
if respond_to?(:session)
|
|
108
|
+
session[:user_id] = user.id
|
|
109
|
+
end
|
|
110
|
+
rescue NoMethodError
|
|
111
|
+
# Session method doesn't exist in this context (e.g., system specs)
|
|
112
|
+
end
|
|
113
|
+
Panda::Core::Current.user = user
|
|
114
|
+
user
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# ============================================================================
|
|
118
|
+
# SYSTEM SPEC HELPERS (HTTP-based Authentication)
|
|
119
|
+
# ============================================================================
|
|
120
|
+
|
|
121
|
+
# For system specs - use test session endpoint (works across processes)
|
|
122
|
+
# This uses the TestSessionsController which is only available in test environment
|
|
123
|
+
#
|
|
124
|
+
# NOTE: Due to Cuprite's redirect handling, we visit the target path directly
|
|
125
|
+
# after setting up the session via the test endpoint. Flash messages won't be
|
|
126
|
+
# available in system tests due to cross-process timing. Use request specs
|
|
127
|
+
# to test flash messages (see authentication_request_spec.rb).
|
|
128
|
+
def login_with_test_endpoint(user, return_to: nil, expect_success: true)
|
|
129
|
+
return_path = return_to || determine_default_redirect_path
|
|
130
|
+
|
|
131
|
+
# Visit the test login endpoint (sets session via Redis)
|
|
132
|
+
# Note: Capybara/Cuprite may not follow the redirect properly, so we
|
|
133
|
+
# manually navigate to the expected destination
|
|
134
|
+
visit "/admin/test_login/#{user.id}?return_to=#{return_path}"
|
|
135
|
+
|
|
136
|
+
# Wait briefly for session to be set
|
|
137
|
+
sleep 0.2
|
|
138
|
+
|
|
139
|
+
# Manually visit the destination since Cuprite doesn't reliably follow redirects
|
|
140
|
+
if expect_success
|
|
141
|
+
visit return_path
|
|
142
|
+
# Wait for page to load
|
|
143
|
+
sleep 0.2
|
|
144
|
+
|
|
145
|
+
# Verify we're on the expected path
|
|
146
|
+
expect(page).to have_current_path(return_path, wait: 2)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Convenience method: Login with Google OAuth provider (using test endpoint)
|
|
151
|
+
def login_with_google(user, expect_success: true)
|
|
152
|
+
login_with_test_endpoint(user, return_to: determine_default_redirect_path, expect_success: expect_success)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Convenience method: Login with GitHub OAuth provider (using test endpoint)
|
|
156
|
+
def login_with_github(user, expect_success: true)
|
|
157
|
+
login_with_test_endpoint(user, return_to: determine_default_redirect_path, expect_success: expect_success)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Convenience method: Login with Microsoft OAuth provider (using test endpoint)
|
|
161
|
+
def login_with_microsoft(user, expect_success: true)
|
|
162
|
+
login_with_test_endpoint(user, return_to: determine_default_redirect_path, expect_success: expect_success)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# High-level helper: Login as admin (creates user if needed)
|
|
166
|
+
def login_as_admin(attributes = {})
|
|
167
|
+
user = create_admin_user(attributes)
|
|
168
|
+
if respond_to?(:visit)
|
|
169
|
+
# System spec - use test endpoint
|
|
170
|
+
login_with_test_endpoint(user, expect_success: true)
|
|
171
|
+
else
|
|
172
|
+
# Request spec - use direct session
|
|
173
|
+
sign_in_as(user)
|
|
174
|
+
end
|
|
175
|
+
user
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# High-level helper: Login as regular user (creates user if needed)
|
|
179
|
+
def login_as_user(attributes = {})
|
|
180
|
+
user = create_regular_user(attributes)
|
|
181
|
+
if respond_to?(:visit)
|
|
182
|
+
# System spec - regular users get redirected to login
|
|
183
|
+
login_with_test_endpoint(user, expect_success: false)
|
|
184
|
+
else
|
|
185
|
+
# Request spec - use direct session
|
|
186
|
+
sign_in_as(user)
|
|
187
|
+
end
|
|
188
|
+
user
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Manual OAuth login (slower, but tests actual OAuth flow)
|
|
192
|
+
# Use this when you need to test the OAuth callback handler itself
|
|
193
|
+
def manual_login_with_oauth(user, provider: :google_oauth2)
|
|
194
|
+
mock_oauth_for_user(user, provider: provider)
|
|
195
|
+
|
|
196
|
+
visit admin_login_path
|
|
197
|
+
expect(page).to have_css("#button-sign-in-#{provider}")
|
|
198
|
+
find("#button-sign-in-#{provider}").click
|
|
199
|
+
|
|
200
|
+
Rails.application.env_config["omniauth.auth"] = OmniAuth.config.mock_auth[provider]
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# ============================================================================
|
|
204
|
+
# PRIVATE HELPER METHODS
|
|
205
|
+
# ============================================================================
|
|
206
|
+
|
|
207
|
+
private
|
|
208
|
+
|
|
209
|
+
def ensure_columns_loaded
|
|
210
|
+
return if @columns_loaded
|
|
211
|
+
Panda::Core::User.connection.schema_cache.clear!
|
|
212
|
+
Panda::Core::User.reset_column_information
|
|
213
|
+
@columns_loaded = true
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def default_image_url(dark: false)
|
|
217
|
+
color = dark ? "%23999" : "%23ccc"
|
|
218
|
+
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100'%3E%3Crect width='100' height='100' fill='#{color}'/%3E%3C/svg%3E"
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def determine_default_redirect_path
|
|
222
|
+
# Check if we're in a CMS context
|
|
223
|
+
if defined?(Panda::CMS)
|
|
224
|
+
"/admin/cms"
|
|
225
|
+
else
|
|
226
|
+
"/admin"
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def admin_login_path
|
|
231
|
+
if defined?(panda_core)
|
|
232
|
+
panda_core.admin_login_path
|
|
233
|
+
else
|
|
234
|
+
"/admin/login"
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def admin_root_path
|
|
239
|
+
if defined?(panda_core)
|
|
240
|
+
panda_core.admin_root_path
|
|
241
|
+
else
|
|
242
|
+
"/admin"
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Configure RSpec to include these helpers in appropriate test types
|
|
250
|
+
RSpec.configure do |config|
|
|
251
|
+
config.include Panda::Core::AuthenticationTestHelpers, type: :request
|
|
252
|
+
config.include Panda::Core::AuthenticationTestHelpers, type: :system
|
|
253
|
+
config.include Panda::Core::AuthenticationTestHelpers, type: :controller
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Configure OmniAuth for testing
|
|
257
|
+
OmniAuth.config.test_mode = true
|
|
258
|
+
OmniAuth.config.on_failure = proc { |env|
|
|
259
|
+
OmniAuth::FailureEndpoint.new(env).redirect_to_failure
|
|
260
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
|
|
3
|
+
module GeneratorSpecHelper
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
before(:each) do
|
|
8
|
+
prepare_destination
|
|
9
|
+
@original_stdout = $stdout
|
|
10
|
+
$stdout = File.new(File::NULL, "w")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
after(:each) do
|
|
14
|
+
FileUtils.rm_rf(destination_root)
|
|
15
|
+
$stdout = @original_stdout
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def destination_root
|
|
20
|
+
@destination_root ||= File.expand_path("../../tmp/generators", __dir__)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def prepare_destination
|
|
24
|
+
FileUtils.rm_rf(destination_root)
|
|
25
|
+
FileUtils.mkdir_p(destination_root)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def run_generator(args = [])
|
|
29
|
+
args = Array(args)
|
|
30
|
+
# Use the generator namespace instead of class name
|
|
31
|
+
generator_name = described_class.namespace
|
|
32
|
+
Rails::Generators.invoke(generator_name, args, destination_root: destination_root)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def generator
|
|
36
|
+
@generator ||= described_class.new([], destination_root: destination_root)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def file_exists?(path)
|
|
40
|
+
File.exist?(File.join(destination_root, path))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def read_file(path)
|
|
44
|
+
File.read(File.join(destination_root, path))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def capture(stream)
|
|
50
|
+
stream = stream.to_s
|
|
51
|
+
captured_stream = StringIO.new
|
|
52
|
+
|
|
53
|
+
# Map stream names to their global variables to avoid eval
|
|
54
|
+
streams = {
|
|
55
|
+
"stdout" => $stdout,
|
|
56
|
+
"stderr" => $stderr,
|
|
57
|
+
"stdin" => $stdin
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
original_stream = streams[stream]
|
|
61
|
+
case stream
|
|
62
|
+
when "stdout"
|
|
63
|
+
$stdout = captured_stream
|
|
64
|
+
when "stderr"
|
|
65
|
+
$stderr = captured_stream
|
|
66
|
+
when "stdin"
|
|
67
|
+
$stdin = captured_stream
|
|
68
|
+
else
|
|
69
|
+
raise ArgumentError, "Unsupported stream: #{stream}"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
yield
|
|
73
|
+
captured_stream.string
|
|
74
|
+
ensure
|
|
75
|
+
case stream
|
|
76
|
+
when "stdout"
|
|
77
|
+
$stdout = original_stream
|
|
78
|
+
when "stderr"
|
|
79
|
+
$stderr = original_stream
|
|
80
|
+
when "stdin"
|
|
81
|
+
$stdin = original_stream
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
RSpec.configure do |config|
|
|
87
|
+
config.include GeneratorSpecHelper, type: :generator
|
|
88
|
+
|
|
89
|
+
# Ensure generator tests have a clean environment
|
|
90
|
+
config.before(:each, type: :generator) do
|
|
91
|
+
prepare_destination
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
config.after(:each, type: :generator) do
|
|
95
|
+
FileUtils.rm_rf(destination_root) if defined?(destination_root)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Configure authentication providers for tests
|
|
4
|
+
RSpec.configure do |config|
|
|
5
|
+
config.before(:each, type: :controller) do
|
|
6
|
+
# Set up test authentication providers
|
|
7
|
+
Panda::Core.configure do |core_config|
|
|
8
|
+
core_config.authentication_providers = {
|
|
9
|
+
google_oauth2: {
|
|
10
|
+
client_id: "test_client_id",
|
|
11
|
+
client_secret: "test_client_secret",
|
|
12
|
+
options: {}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tempfile"
|
|
4
|
+
require "base64"
|
|
5
|
+
|
|
6
|
+
# Stub services that make external HTTP requests to prevent them in tests
|
|
7
|
+
RSpec.configure do |config|
|
|
8
|
+
config.before(:each) do
|
|
9
|
+
# Stub URI.open to prevent external HTTP requests when downloading avatars
|
|
10
|
+
# This affects AttachAvatarService which downloads avatars from OAuth providers
|
|
11
|
+
# Tests that specifically need real HTTP requests can override this stub
|
|
12
|
+
allow(URI).to receive(:open).and_wrap_original do |original_method, *args, **kwargs, &block|
|
|
13
|
+
url = args[0]
|
|
14
|
+
|
|
15
|
+
# Only stub avatar downloads from OAuth providers and test URLs
|
|
16
|
+
if /googleusercontent\.com|githubusercontent\.com|graph\.microsoft\.com|example\.com/.match?(url.to_s)
|
|
17
|
+
# Use the actual test fixture file instead of creating a fake file
|
|
18
|
+
# This ensures compatibility with Active Storage
|
|
19
|
+
fixture_path = Panda::Core::Engine.root.join("spec", "fixtures", "files", "test_image.jpg")
|
|
20
|
+
downloaded_file = File.open(fixture_path, "rb")
|
|
21
|
+
|
|
22
|
+
# Add methods that AttachAvatarService expects
|
|
23
|
+
downloaded_file.define_singleton_method(:content_type) { "image/jpeg" }
|
|
24
|
+
downloaded_file.define_singleton_method(:size) { File.size(fixture_path) }
|
|
25
|
+
|
|
26
|
+
# Call the block with our file if a block is given
|
|
27
|
+
if block_given?
|
|
28
|
+
result = block.call(downloaded_file)
|
|
29
|
+
downloaded_file.close unless downloaded_file.closed?
|
|
30
|
+
result
|
|
31
|
+
else
|
|
32
|
+
downloaded_file
|
|
33
|
+
end
|
|
34
|
+
else
|
|
35
|
+
# For other URLs, use the original method
|
|
36
|
+
original_method.call(*args, **kwargs, &block)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
data/lib/panda/core/version.rb
CHANGED
data/lib/panda/core.rb
CHANGED
|
@@ -15,4 +15,6 @@ require_relative "core/configuration"
|
|
|
15
15
|
require_relative "core/asset_loader"
|
|
16
16
|
require_relative "core/debug"
|
|
17
17
|
require_relative "core/services/base_service"
|
|
18
|
+
require_relative "core/shared/inflections_config"
|
|
19
|
+
require_relative "core/shared/generator_config"
|
|
18
20
|
require_relative "core/engine" if defined?(Rails)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: panda-core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.7.
|
|
4
|
+
version: 0.7.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Otaina Limited
|
|
@@ -461,6 +461,15 @@ files:
|
|
|
461
461
|
- lib/panda/core/configuration.rb
|
|
462
462
|
- lib/panda/core/debug.rb
|
|
463
463
|
- lib/panda/core/engine.rb
|
|
464
|
+
- lib/panda/core/engine/admin_controller_config.rb
|
|
465
|
+
- lib/panda/core/engine/autoload_config.rb
|
|
466
|
+
- lib/panda/core/engine/generator_config.rb
|
|
467
|
+
- lib/panda/core/engine/importmap_config.rb
|
|
468
|
+
- lib/panda/core/engine/inflections_config.rb
|
|
469
|
+
- lib/panda/core/engine/middleware_config.rb
|
|
470
|
+
- lib/panda/core/engine/omniauth_config.rb
|
|
471
|
+
- lib/panda/core/engine/phlex_config.rb
|
|
472
|
+
- lib/panda/core/engine/test_config.rb
|
|
464
473
|
- lib/panda/core/media.rb
|
|
465
474
|
- lib/panda/core/notifications.rb
|
|
466
475
|
- lib/panda/core/oauth_providers.rb
|
|
@@ -468,9 +477,18 @@ files:
|
|
|
468
477
|
- lib/panda/core/rake_tasks.rb
|
|
469
478
|
- lib/panda/core/seo.rb
|
|
470
479
|
- lib/panda/core/services/base_service.rb
|
|
480
|
+
- lib/panda/core/shared/generator_config.rb
|
|
481
|
+
- lib/panda/core/shared/inflections_config.rb
|
|
471
482
|
- lib/panda/core/sluggable.rb
|
|
472
483
|
- lib/panda/core/subscribers/authentication_subscriber.rb
|
|
473
484
|
- lib/panda/core/testing/rails_helper.rb
|
|
485
|
+
- lib/panda/core/testing/support/authentication_helpers.rb
|
|
486
|
+
- lib/panda/core/testing/support/authentication_test_helpers.rb
|
|
487
|
+
- lib/panda/core/testing/support/generator_spec_helper.rb
|
|
488
|
+
- lib/panda/core/testing/support/html_helpers.rb
|
|
489
|
+
- lib/panda/core/testing/support/omniauth_setup.rb
|
|
490
|
+
- lib/panda/core/testing/support/service_stubs.rb
|
|
491
|
+
- lib/panda/core/testing/support/setup.rb
|
|
474
492
|
- lib/panda/core/version.rb
|
|
475
493
|
- lib/tasks/assets.rake
|
|
476
494
|
- lib/tasks/panda/core/migrations.rake
|