paquette 0.1.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 +7 -0
- data/LICENSE.md +10 -0
- data/README.md +90 -0
- data/lib/paquette/gem_server/directory_gem_repository.rb +154 -0
- data/lib/paquette/gem_server/gem_repacker.rb +170 -0
- data/lib/paquette/gem_server/gem_repository.rb +53 -0
- data/lib/paquette/gem_server/personalizer.rb +66 -0
- data/lib/paquette/gem_server/read_gated_repository.rb +74 -0
- data/lib/paquette/gem_server.rb +393 -0
- data/lib/paquette/npm_repacker.rb +153 -0
- data/lib/paquette/npm_server/directory_npm_repository.rb +127 -0
- data/lib/paquette/npm_server/gated_npm_repository.rb +66 -0
- data/lib/paquette/npm_server/npm_repository.rb +53 -0
- data/lib/paquette/npm_server.rb +98 -0
- data/lib/paquette/routes.rb +78 -0
- data/lib/paquette/subdomain_router.rb +59 -0
- data/lib/paquette/version.rb +3 -0
- data/lib/paquette.rb +12 -0
- data/test/fixtures/gems/minuscule_test/minuscule_test-0.1.0.gem +0 -0
- data/test/fixtures/gems/zip_kit/zip_kit-6.2.0.gem +0 -0
- data/test/fixtures/gems/zip_kit/zip_kit-6.2.1.gem +0 -0
- data/test/fixtures/npm/react-dropzone/react-dropzone-14.3.8.tgz +0 -0
- data/test/gem_server/bundler_install_test.rb +86 -0
- data/test/gem_server/directory_gem_repository_test.rb +208 -0
- data/test/gem_server/gem_repacker_test.rb +289 -0
- data/test/gem_server/personalizer_metadata_test.rb +49 -0
- data/test/gem_server/personalizer_test.rb +66 -0
- data/test/gem_server/read_gated_repository_test.rb +254 -0
- data/test/gem_server/repository_stack_test.rb +71 -0
- data/test/gem_server_test.rb +339 -0
- data/test/npm_repacker_sourcemap_test.rb +108 -0
- data/test/npm_repacker_test.rb +117 -0
- data/test/npm_server_test.rb +140 -0
- data/test/routes_test.rb +142 -0
- data/test/subdomain_router_integration_test.rb +126 -0
- data/test/subdomain_router_test.rb +125 -0
- data/test/test_helper.rb +12 -0
- metadata +117 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
require_relative "test_helper"
|
|
2
|
+
|
|
3
|
+
class NpmServerTest < Minitest::Test
|
|
4
|
+
include Rack::Test::Methods
|
|
5
|
+
|
|
6
|
+
def setup
|
|
7
|
+
# Create a temporary directory for test packages
|
|
8
|
+
@test_packages_dir = Dir.mktmpdir("paquette_npm_test")
|
|
9
|
+
@app = Paquette::NpmServer.new(@test_packages_dir)
|
|
10
|
+
|
|
11
|
+
# Create some test packages
|
|
12
|
+
create_test_packages
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def teardown
|
|
16
|
+
FileUtils.rm_rf(@test_packages_dir) if @test_packages_dir
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
attr_reader :app
|
|
20
|
+
|
|
21
|
+
def test_root_endpoint
|
|
22
|
+
get "/"
|
|
23
|
+
assert_equal 200, last_response.status
|
|
24
|
+
assert_equal "Paquette NPM Repository", last_response.body
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_ping_endpoint
|
|
28
|
+
get "/-/ping"
|
|
29
|
+
assert_equal 200, last_response.status
|
|
30
|
+
assert_equal "application/json", last_response.content_type
|
|
31
|
+
|
|
32
|
+
response = JSON.parse(last_response.body)
|
|
33
|
+
assert_equal({}, response)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def test_whoami_endpoint
|
|
37
|
+
get "/-/whoami"
|
|
38
|
+
assert_equal 200, last_response.status
|
|
39
|
+
assert_equal "application/json", last_response.content_type
|
|
40
|
+
|
|
41
|
+
response = JSON.parse(last_response.body)
|
|
42
|
+
assert_equal "paquette", response["username"]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_package_metadata
|
|
46
|
+
get "/test-package"
|
|
47
|
+
assert_equal 200, last_response.status
|
|
48
|
+
assert_equal "application/json", last_response.content_type
|
|
49
|
+
|
|
50
|
+
metadata = JSON.parse(last_response.body)
|
|
51
|
+
assert_equal "test-package", metadata["name"]
|
|
52
|
+
assert metadata["versions"].is_a?(Hash)
|
|
53
|
+
assert metadata["versions"].key?("1.0.0")
|
|
54
|
+
assert metadata["versions"].key?("1.1.0")
|
|
55
|
+
assert_equal "1.1.0", metadata["dist-tags"]["latest"]
|
|
56
|
+
assert metadata["time"].is_a?(Hash)
|
|
57
|
+
assert metadata["time"].key?("1.0.0")
|
|
58
|
+
assert metadata["time"].key?("1.1.0")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def test_package_metadata_nonexistent
|
|
62
|
+
get "/nonexistent-package"
|
|
63
|
+
assert_equal 404, last_response.status
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def test_dist_tags
|
|
67
|
+
get "/-/package/test-package/dist-tags"
|
|
68
|
+
assert_equal 200, last_response.status
|
|
69
|
+
assert_equal "application/json", last_response.content_type
|
|
70
|
+
|
|
71
|
+
dist_tags = JSON.parse(last_response.body)
|
|
72
|
+
assert_equal "1.1.0", dist_tags["latest"]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def test_dist_tags_nonexistent
|
|
76
|
+
get "/-/package/nonexistent-package/dist-tags"
|
|
77
|
+
assert_equal 404, last_response.status
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_package_download
|
|
81
|
+
get "/test-package/test-package-1.0.0.tgz"
|
|
82
|
+
assert_equal 200, last_response.status
|
|
83
|
+
assert_equal "application/octet-stream", last_response.content_type
|
|
84
|
+
assert last_response.body.length > 0
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def test_package_download_nonexistent
|
|
88
|
+
get "/test-package/test-package-999.0.0.tgz"
|
|
89
|
+
assert_equal 404, last_response.status
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def test_package_download_invalid_filename
|
|
93
|
+
get "/test-package/invalid-filename.tgz"
|
|
94
|
+
assert_equal 404, last_response.status
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def test_multiple_packages
|
|
98
|
+
get "/another-package"
|
|
99
|
+
assert_equal 200, last_response.status
|
|
100
|
+
|
|
101
|
+
metadata = JSON.parse(last_response.body)
|
|
102
|
+
assert_equal "another-package", metadata["name"]
|
|
103
|
+
assert metadata["versions"].key?("2.0.0")
|
|
104
|
+
assert_equal "2.0.0", metadata["dist-tags"]["latest"]
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def test_package_versions_structure
|
|
108
|
+
get "/test-package"
|
|
109
|
+
metadata = JSON.parse(last_response.body)
|
|
110
|
+
|
|
111
|
+
# Check version structure
|
|
112
|
+
version_1_0_0 = metadata["versions"]["1.0.0"]
|
|
113
|
+
assert_equal "test-package", version_1_0_0["name"]
|
|
114
|
+
assert_equal "1.0.0", version_1_0_0["version"]
|
|
115
|
+
assert version_1_0_0["dist"].is_a?(Hash)
|
|
116
|
+
assert_equal "test-package/test-package-1.0.0.tgz", version_1_0_0["dist"]["tarball"]
|
|
117
|
+
|
|
118
|
+
version_1_1_0 = metadata["versions"]["1.1.0"]
|
|
119
|
+
assert_equal "test-package", version_1_1_0["name"]
|
|
120
|
+
assert_equal "1.1.0", version_1_1_0["version"]
|
|
121
|
+
assert_equal "test-package/test-package-1.1.0.tgz", version_1_1_0["dist"]["tarball"]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
private
|
|
125
|
+
|
|
126
|
+
def create_test_packages
|
|
127
|
+
# Create test-package with versions 1.0.0 and 1.1.0
|
|
128
|
+
package_dir = File.join(@test_packages_dir, "test-package")
|
|
129
|
+
FileUtils.mkdir_p(package_dir)
|
|
130
|
+
|
|
131
|
+
# Create dummy .tgz files
|
|
132
|
+
File.write(File.join(package_dir, "test-package-1.0.0.tgz"), "dummy tarball content 1.0.0")
|
|
133
|
+
File.write(File.join(package_dir, "test-package-1.1.0.tgz"), "dummy tarball content 1.1.0")
|
|
134
|
+
|
|
135
|
+
# Create another-package with version 2.0.0
|
|
136
|
+
another_package_dir = File.join(@test_packages_dir, "another-package")
|
|
137
|
+
FileUtils.mkdir_p(another_package_dir)
|
|
138
|
+
File.write(File.join(another_package_dir, "another-package-2.0.0.tgz"), "dummy tarball content 2.0.0")
|
|
139
|
+
end
|
|
140
|
+
end
|
data/test/routes_test.rb
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
require_relative "test_helper"
|
|
2
|
+
|
|
3
|
+
class RoutesTest < Minitest::Test
|
|
4
|
+
include Rack::Test::Methods
|
|
5
|
+
|
|
6
|
+
class TestApp
|
|
7
|
+
attr_reader :last_params, :last_action
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@last_params = nil
|
|
11
|
+
@last_action = nil
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call(env)
|
|
15
|
+
request = Rack::Request.new(env)
|
|
16
|
+
|
|
17
|
+
if (route = @routes.match(request))
|
|
18
|
+
@last_params = route.params(request)
|
|
19
|
+
@last_action = @routes.perform_action(route, self, request)
|
|
20
|
+
else
|
|
21
|
+
[404, {}, ["Not Found"]]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def setup_routes
|
|
26
|
+
@routes = Paquette::Routes.draw do |r|
|
|
27
|
+
r.get "/" do
|
|
28
|
+
[200, {}, ["Root"]]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
r.get "/hello/:name" do |name:|
|
|
32
|
+
[200, {}, ["Hello #{name}"]]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
r.get "/gem/stuff/:some_param.gz" do |some_param:|
|
|
36
|
+
[200, {}, ["Gem stuff with param: #{some_param}"]]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
r.post "/api/data" do
|
|
40
|
+
[201, {}, ["Data created"]]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
r.get "/api/version/:version" do |version:|
|
|
44
|
+
[200, {}, ["Version: #{version}"]]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def setup
|
|
51
|
+
@app = TestApp.new
|
|
52
|
+
@app.setup_routes
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
attr_reader :app
|
|
56
|
+
|
|
57
|
+
def test_root_route
|
|
58
|
+
get "/"
|
|
59
|
+
assert_equal 200, last_response.status
|
|
60
|
+
assert_equal "Root", last_response.body
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def test_hello_route_with_params
|
|
64
|
+
get "/hello/world"
|
|
65
|
+
assert_equal 200, last_response.status
|
|
66
|
+
assert_equal "Hello world", last_response.body
|
|
67
|
+
assert_equal "world", app.last_params["name"]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def test_gem_stuff_route_with_params
|
|
71
|
+
get "/gem/stuff/test-param.gz"
|
|
72
|
+
assert_equal 200, last_response.status
|
|
73
|
+
assert_equal "Gem stuff with param: test-param", last_response.body
|
|
74
|
+
assert_equal "test-param", app.last_params["some_param"]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def test_post_route
|
|
78
|
+
post "/api/data"
|
|
79
|
+
assert_equal 201, last_response.status
|
|
80
|
+
assert_equal "Data created", last_response.body
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def test_version_route_with_params
|
|
84
|
+
get "/api/version/1.2.3"
|
|
85
|
+
assert_equal 200, last_response.status
|
|
86
|
+
assert_equal "Version: 1.2.3", last_response.body
|
|
87
|
+
assert_equal "1.2.3", app.last_params["version"]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def test_nonexistent_route
|
|
91
|
+
get "/nonexistent"
|
|
92
|
+
assert_equal 404, last_response.status
|
|
93
|
+
assert_equal "Not Found", last_response.body
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def test_wrong_method
|
|
97
|
+
post "/hello/world"
|
|
98
|
+
assert_equal 404, last_response.status
|
|
99
|
+
assert_equal "Not Found", last_response.body
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def test_routes_class_draw_method
|
|
103
|
+
routes = Paquette::Routes.draw do |r|
|
|
104
|
+
r.get "/test" do
|
|
105
|
+
[200, {}, ["Test"]]
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
assert_instance_of Paquette::Routes, routes
|
|
110
|
+
assert_equal 1, routes.instance_variable_get(:@routes).length
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def test_route_builder_methods
|
|
114
|
+
routes = Paquette::Routes.draw do |r|
|
|
115
|
+
r.get("/get") { [200, {}, ["GET"]] }
|
|
116
|
+
r.post("/post") { [200, {}, ["POST"]] }
|
|
117
|
+
r.put("/put") { [200, {}, ["PUT"]] }
|
|
118
|
+
r.delete("/delete") { [200, {}, ["DELETE"]] }
|
|
119
|
+
r.patch("/patch") { [200, {}, ["PATCH"]] }
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
assert_equal 5, routes.instance_variable_get(:@routes).length
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def test_instance_exec_in_route_blocks
|
|
126
|
+
test_instance = self
|
|
127
|
+
|
|
128
|
+
routes = Paquette::Routes.draw do |r|
|
|
129
|
+
r.get "/self-test" do
|
|
130
|
+
[200, {}, ["Instance: #{self.class}"]]
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Create a mock request
|
|
135
|
+
request = Rack::Request.new(Rack::MockRequest.env_for("/self-test"))
|
|
136
|
+
route = routes.match(request)
|
|
137
|
+
|
|
138
|
+
assert route
|
|
139
|
+
result = routes.perform_action(route, test_instance, request)
|
|
140
|
+
assert_equal [200, {}, ["Instance: RoutesTest"]], result
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
require_relative "test_helper"
|
|
2
|
+
|
|
3
|
+
class SubdomainRouterIntegrationTest < Minitest::Test
|
|
4
|
+
include Rack::Test::Methods
|
|
5
|
+
|
|
6
|
+
def setup
|
|
7
|
+
# No need to set environment variables since we use the packages directory structure
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def app
|
|
11
|
+
# Load config.ru with the test fixtures dir so we don't depend on
|
|
12
|
+
# whatever a dev push may have left in packages/.
|
|
13
|
+
ENV["PAQUETTE_PACKAGES_DIR"] = File.expand_path("fixtures", __dir__)
|
|
14
|
+
Rack::Builder.parse_file(File.expand_path("../config.ru", __dir__))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_gem_subdomain_routing
|
|
18
|
+
get "/", {}, {"HTTP_HOST" => "gem.example.com"}
|
|
19
|
+
assert_equal 200, last_response.status
|
|
20
|
+
assert_equal "Paquette RubyGems Repository", last_response.body
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_gem_subdomain_api_endpoints
|
|
24
|
+
get "/api/v1/names", {}, {"HTTP_HOST" => "gem.example.com"}
|
|
25
|
+
assert_equal 200, last_response.status
|
|
26
|
+
assert_equal "application/json", last_response.content_type
|
|
27
|
+
|
|
28
|
+
names = JSON.parse(last_response.body)
|
|
29
|
+
assert_equal ["minuscule_test", "zip_kit"], names.sort
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_gem_subdomain_specs_endpoint
|
|
33
|
+
get "/specs.4.8", {}, {"HTTP_HOST" => "gem.example.com"}
|
|
34
|
+
assert_equal 200, last_response.status
|
|
35
|
+
assert_equal "application/octet-stream", last_response.content_type
|
|
36
|
+
|
|
37
|
+
specs = Marshal.load(last_response.body)
|
|
38
|
+
assert specs.is_a?(Array)
|
|
39
|
+
assert_equal 3, specs.length
|
|
40
|
+
|
|
41
|
+
gem_names = specs.map { |spec| spec[0] }.sort.uniq
|
|
42
|
+
assert_equal ["minuscule_test", "zip_kit"], gem_names
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_npm_subdomain_routing
|
|
46
|
+
# Test that requests to npm.example.com are routed to NpmServer
|
|
47
|
+
get "/", {}, {"HTTP_HOST" => "npm.example.com"}
|
|
48
|
+
assert_equal 200, last_response.status
|
|
49
|
+
assert_equal "Paquette NPM Repository", last_response.body
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_npm_subdomain_ping_endpoint
|
|
53
|
+
# Test that npm ping endpoint works with npm subdomain
|
|
54
|
+
get "/-/ping", {}, {"HTTP_HOST" => "npm.example.com"}
|
|
55
|
+
assert_equal 200, last_response.status
|
|
56
|
+
assert_equal "application/json", last_response.content_type
|
|
57
|
+
|
|
58
|
+
response = JSON.parse(last_response.body)
|
|
59
|
+
assert_equal({}, response)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_npm_subdomain_package_metadata
|
|
63
|
+
# Test that package metadata works with npm subdomain
|
|
64
|
+
get "/react-dropzone", {}, {"HTTP_HOST" => "npm.example.com"}
|
|
65
|
+
assert_equal 200, last_response.status
|
|
66
|
+
assert_equal "application/json", last_response.content_type
|
|
67
|
+
|
|
68
|
+
metadata = JSON.parse(last_response.body)
|
|
69
|
+
assert_equal "react-dropzone", metadata["name"]
|
|
70
|
+
assert metadata["versions"].is_a?(Hash)
|
|
71
|
+
assert metadata["versions"].key?("14.3.8")
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def test_npm_subdomain_package_download
|
|
75
|
+
# Test that package download works with npm subdomain
|
|
76
|
+
get "/react-dropzone/react-dropzone-14.3.8.tgz", {}, {"HTTP_HOST" => "npm.example.com"}
|
|
77
|
+
assert_equal 200, last_response.status
|
|
78
|
+
assert_equal "application/octet-stream", last_response.content_type
|
|
79
|
+
assert last_response.body.length > 0
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def test_unknown_subdomain_fallback
|
|
83
|
+
# Test that unknown subdomains get the fallback response
|
|
84
|
+
get "/", {}, {"HTTP_HOST" => "api.example.com"}
|
|
85
|
+
assert_equal 404, last_response.status
|
|
86
|
+
assert_equal "Need subdomain gem/npm", last_response.body
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def test_no_subdomain_fallback
|
|
90
|
+
# Test that requests without subdomain get the fallback response
|
|
91
|
+
get "/", {}, {"HTTP_HOST" => "example.com"}
|
|
92
|
+
assert_equal 404, last_response.status
|
|
93
|
+
assert_equal "Need subdomain gem/npm", last_response.body
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def test_localhost_with_port_gem_subdomain
|
|
97
|
+
# Test that localhost with port works for gem subdomain
|
|
98
|
+
get "/", {}, {"HTTP_HOST" => "gem.localhost:9292"}
|
|
99
|
+
assert_equal 200, last_response.status
|
|
100
|
+
assert_equal "Paquette RubyGems Repository", last_response.body
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def test_localhost_with_port_npm_subdomain
|
|
104
|
+
# Test that localhost with port works for npm subdomain
|
|
105
|
+
get "/", {}, {"HTTP_HOST" => "npm.localhost:9292"}
|
|
106
|
+
assert_equal 200, last_response.status
|
|
107
|
+
assert_equal "Paquette NPM Repository", last_response.body
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def test_gem_subdomain_gem_download
|
|
111
|
+
get "/gems/zip_kit-6.2.0.gem", {}, {"HTTP_HOST" => "gem.example.com"}
|
|
112
|
+
assert_equal 200, last_response.status
|
|
113
|
+
assert_equal "application/octet-stream", last_response.content_type
|
|
114
|
+
assert last_response.body.length > 0
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def test_cross_subdomain_isolation
|
|
118
|
+
# Test that npm endpoints don't work on gem subdomain
|
|
119
|
+
get "/-/ping", {}, {"HTTP_HOST" => "gem.example.com"}
|
|
120
|
+
assert_equal 404, last_response.status
|
|
121
|
+
|
|
122
|
+
# Test that gem endpoints don't work on npm subdomain
|
|
123
|
+
get "/api/v1/names", {}, {"HTTP_HOST" => "npm.example.com"}
|
|
124
|
+
assert_equal 404, last_response.status
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
require_relative "test_helper"
|
|
2
|
+
|
|
3
|
+
class SubdomainRouterTest < Minitest::Test
|
|
4
|
+
def setup
|
|
5
|
+
@mock_app = MockApp.new
|
|
6
|
+
@gem_server = Paquette::GemServer.new(Paquette::GemServer::DirectoryGemRepository.new(FIXTURE_GEMS_DIR))
|
|
7
|
+
@npm_server = Paquette::NpmServer.new(FIXTURE_NPM_DIR)
|
|
8
|
+
@router = Paquette::SubdomainRouter.new do |router|
|
|
9
|
+
router.map "gems", to: @gem_server
|
|
10
|
+
router.map "npm", to: @npm_server
|
|
11
|
+
router.fallback to: @mock_app
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def test_extract_subdomain_with_gems_subdomain
|
|
16
|
+
test_cases = [
|
|
17
|
+
["gems.example.com", "gems"],
|
|
18
|
+
["gems.localhost", "gems"],
|
|
19
|
+
["gems.localhost:9292", "gems"]
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
test_cases.each do |host, expected|
|
|
23
|
+
result = @router.send(:extract_subdomain, host)
|
|
24
|
+
assert_equal expected, result, "Expected '#{expected}' for host '#{host}', got '#{result}'"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_extract_subdomain_with_npm_subdomain
|
|
29
|
+
test_cases = [
|
|
30
|
+
["npm.example.com", "npm"],
|
|
31
|
+
["npm.localhost", "npm"],
|
|
32
|
+
["npm.localhost:9292", "npm"]
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
test_cases.each do |host, expected|
|
|
36
|
+
result = @router.send(:extract_subdomain, host)
|
|
37
|
+
assert_equal expected, result, "Expected '#{expected}' for host '#{host}', got '#{result}'"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_extract_subdomain_with_no_subdomain
|
|
42
|
+
test_cases = [
|
|
43
|
+
["localhost", nil],
|
|
44
|
+
["localhost:9292", nil],
|
|
45
|
+
["example.com", nil],
|
|
46
|
+
["api.example.com", nil] # unknown subdomain
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
test_cases.each do |host, expected|
|
|
50
|
+
result = @router.send(:extract_subdomain, host)
|
|
51
|
+
if expected.nil?
|
|
52
|
+
assert_nil result, "Expected nil for host '#{host}', got '#{result}'"
|
|
53
|
+
else
|
|
54
|
+
assert_equal expected, result, "Expected '#{expected}' for host '#{host}', got '#{result}'"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def test_routing_to_gems_server
|
|
60
|
+
env = create_env("gems.example.com", "/")
|
|
61
|
+
|
|
62
|
+
# Mock the GemServer instance to verify it's called
|
|
63
|
+
gem_server_mock = Minitest::Mock.new
|
|
64
|
+
gem_server_mock.expect :call, [200, {}, ["GemServer response"]], [env]
|
|
65
|
+
|
|
66
|
+
# Stub the instance method
|
|
67
|
+
@gem_server.stub :call, ->(env) { gem_server_mock.call(env) } do
|
|
68
|
+
response = @router.call(env)
|
|
69
|
+
assert_equal [200, {}, ["GemServer response"]], response
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
gem_server_mock.verify
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def test_routing_to_npm_server
|
|
76
|
+
env = create_env("npm.example.com", "/")
|
|
77
|
+
|
|
78
|
+
# Mock the NpmServer instance to verify it's called
|
|
79
|
+
npm_server_mock = Minitest::Mock.new
|
|
80
|
+
npm_server_mock.expect :call, [200, {}, ["NpmServer response"]], [env]
|
|
81
|
+
|
|
82
|
+
# Stub the instance method
|
|
83
|
+
@npm_server.stub :call, ->(env) { npm_server_mock.call(env) } do
|
|
84
|
+
response = @router.call(env)
|
|
85
|
+
assert_equal [200, {}, ["NpmServer response"]], response
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
npm_server_mock.verify
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def test_routing_to_default_app
|
|
92
|
+
env = create_env("example.com", "/")
|
|
93
|
+
|
|
94
|
+
# The router should call the default app
|
|
95
|
+
response = @router.call(env)
|
|
96
|
+
assert_equal [200, {}, ["Default app response"]], response
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def test_fallback_behavior
|
|
100
|
+
env = create_env("unknown.example.com", "/")
|
|
101
|
+
|
|
102
|
+
# The router should call the fallback app
|
|
103
|
+
response = @router.call(env)
|
|
104
|
+
assert_equal [200, {}, ["Default app response"]], response
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
def create_env(host, path)
|
|
110
|
+
{
|
|
111
|
+
"HTTP_HOST" => host,
|
|
112
|
+
"REQUEST_METHOD" => "GET",
|
|
113
|
+
"PATH_INFO" => path,
|
|
114
|
+
"QUERY_STRING" => "",
|
|
115
|
+
"rack.input" => StringIO.new
|
|
116
|
+
}
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Mock app for testing
|
|
120
|
+
class MockApp
|
|
121
|
+
def call(env)
|
|
122
|
+
[200, {}, ["Default app response"]]
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require "minitest/autorun"
|
|
2
|
+
require "rack/test"
|
|
3
|
+
require "rack"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "json"
|
|
6
|
+
require "stringio"
|
|
7
|
+
require "net/http"
|
|
8
|
+
require "tempfile"
|
|
9
|
+
require_relative "../lib/paquette"
|
|
10
|
+
|
|
11
|
+
FIXTURE_GEMS_DIR = File.expand_path("fixtures/gems", __dir__)
|
|
12
|
+
FIXTURE_NPM_DIR = File.expand_path("fixtures/npm", __dir__)
|
metadata
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: paquette
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Julik Tarkhanov
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2026-04-26 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rack
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '3.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '3.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: mustermann
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '3.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '3.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: sourcemap
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0.1'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0.1'
|
|
54
|
+
executables: []
|
|
55
|
+
extensions: []
|
|
56
|
+
extra_rdoc_files: []
|
|
57
|
+
files:
|
|
58
|
+
- LICENSE.md
|
|
59
|
+
- README.md
|
|
60
|
+
- lib/paquette.rb
|
|
61
|
+
- lib/paquette/gem_server.rb
|
|
62
|
+
- lib/paquette/gem_server/directory_gem_repository.rb
|
|
63
|
+
- lib/paquette/gem_server/gem_repacker.rb
|
|
64
|
+
- lib/paquette/gem_server/gem_repository.rb
|
|
65
|
+
- lib/paquette/gem_server/personalizer.rb
|
|
66
|
+
- lib/paquette/gem_server/read_gated_repository.rb
|
|
67
|
+
- lib/paquette/npm_repacker.rb
|
|
68
|
+
- lib/paquette/npm_server.rb
|
|
69
|
+
- lib/paquette/npm_server/directory_npm_repository.rb
|
|
70
|
+
- lib/paquette/npm_server/gated_npm_repository.rb
|
|
71
|
+
- lib/paquette/npm_server/npm_repository.rb
|
|
72
|
+
- lib/paquette/routes.rb
|
|
73
|
+
- lib/paquette/subdomain_router.rb
|
|
74
|
+
- lib/paquette/version.rb
|
|
75
|
+
- test/fixtures/gems/minuscule_test/minuscule_test-0.1.0.gem
|
|
76
|
+
- test/fixtures/gems/zip_kit/zip_kit-6.2.0.gem
|
|
77
|
+
- test/fixtures/gems/zip_kit/zip_kit-6.2.1.gem
|
|
78
|
+
- test/fixtures/npm/react-dropzone/react-dropzone-14.3.8.tgz
|
|
79
|
+
- test/gem_server/bundler_install_test.rb
|
|
80
|
+
- test/gem_server/directory_gem_repository_test.rb
|
|
81
|
+
- test/gem_server/gem_repacker_test.rb
|
|
82
|
+
- test/gem_server/personalizer_metadata_test.rb
|
|
83
|
+
- test/gem_server/personalizer_test.rb
|
|
84
|
+
- test/gem_server/read_gated_repository_test.rb
|
|
85
|
+
- test/gem_server/repository_stack_test.rb
|
|
86
|
+
- test/gem_server_test.rb
|
|
87
|
+
- test/npm_repacker_sourcemap_test.rb
|
|
88
|
+
- test/npm_repacker_test.rb
|
|
89
|
+
- test/npm_server_test.rb
|
|
90
|
+
- test/routes_test.rb
|
|
91
|
+
- test/subdomain_router_integration_test.rb
|
|
92
|
+
- test/subdomain_router_test.rb
|
|
93
|
+
- test/test_helper.rb
|
|
94
|
+
homepage: https://github.com/julik/paquette
|
|
95
|
+
licenses:
|
|
96
|
+
- Osassy
|
|
97
|
+
metadata:
|
|
98
|
+
homepage_uri: https://github.com/julik/paquette
|
|
99
|
+
source_code_uri: https://github.com/julik/paquette
|
|
100
|
+
rdoc_options: []
|
|
101
|
+
require_paths:
|
|
102
|
+
- lib
|
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
104
|
+
requirements:
|
|
105
|
+
- - ">="
|
|
106
|
+
- !ruby/object:Gem::Version
|
|
107
|
+
version: '3.1'
|
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
|
+
requirements:
|
|
110
|
+
- - ">="
|
|
111
|
+
- !ruby/object:Gem::Version
|
|
112
|
+
version: '0'
|
|
113
|
+
requirements: []
|
|
114
|
+
rubygems_version: 3.6.2
|
|
115
|
+
specification_version: 4
|
|
116
|
+
summary: Rack-based server for gated gem and NPM package repositories
|
|
117
|
+
test_files: []
|