mudis 0.8.0 → 0.9.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.
@@ -1,137 +1,147 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "spec_helper"
4
-
5
- RSpec.describe MudisClient do # rubocop:disable Metrics/BlockLength
6
- let(:socket_path) { "/tmp/mudis.sock" }
7
- let(:mock_socket) { instance_double(UNIXSocket) }
8
- let(:client) { MudisClient.new }
9
-
10
- around do |example|
11
- ClimateControl.modify("SOCKET_PATH" => socket_path) do
12
- example.run
13
- end
14
- end
15
-
16
- before do
17
- allow(UNIXSocket).to receive(:open).and_yield(mock_socket)
18
- end
19
-
20
- describe "#read" do
21
- it "sends a read command and returns the value" do
22
- payload = { cmd: "read", key: "test_key", namespace: nil }
23
- response = { ok: true, value: "test_value" }.to_json
24
-
25
- expect(mock_socket).to receive(:puts).with(payload.to_json)
26
- expect(mock_socket).to receive(:gets).and_return(response)
27
-
28
- expect(client.read("test_key")).to eq("test_value")
29
- end
30
- end
31
-
32
- describe "#write" do
33
- it "sends a write command and returns the value" do
34
- payload = { cmd: "write", key: "test_key", value: "test_value", ttl: nil, namespace: nil }
35
- response = { ok: true, value: nil }.to_json
36
-
37
- expect(mock_socket).to receive(:puts).with(payload.to_json)
38
- expect(mock_socket).to receive(:gets).and_return(response)
39
-
40
- expect(client.write("test_key", "test_value")).to be_nil
41
- end
42
- end
43
-
44
- describe "#delete" do
45
- it "sends a delete command and returns the value" do
46
- payload = { cmd: "delete", key: "test_key", namespace: nil }
47
- response = { ok: true, value: nil }.to_json
48
-
49
- expect(mock_socket).to receive(:puts).with(payload.to_json)
50
- expect(mock_socket).to receive(:gets).and_return(response)
51
-
52
- expect(client.delete("test_key")).to be_nil
53
- end
54
- end
55
-
56
- describe "#exists?" do
57
- it "sends an exists command and returns true" do
58
- payload = { cmd: "exists", key: "test_key", namespace: nil }
59
- response = { ok: true, value: true }.to_json
60
-
61
- expect(mock_socket).to receive(:puts).with(payload.to_json)
62
- expect(mock_socket).to receive(:gets).and_return(response)
63
-
64
- expect(client.exists?("test_key")).to eq(true)
65
- end
66
- end
67
-
68
- describe "#fetch" do
69
- it "fetches an existing value or writes a new one" do
70
- read_response = { ok: true, value: nil }.to_json
71
- write_payload = { cmd: "write", key: "test_key", value: "new_value", ttl: nil, namespace: nil }
72
- write_response = { ok: true, value: nil }.to_json
73
-
74
- expect(mock_socket).to receive(:puts).with({ cmd: "read", key: "test_key", namespace: nil }.to_json)
75
- expect(mock_socket).to receive(:gets).and_return(read_response)
76
- expect(mock_socket).to receive(:puts).with(write_payload.to_json)
77
- expect(mock_socket).to receive(:gets).and_return(write_response)
78
-
79
- result = client.fetch("test_key") { "new_value" } # rubocop:disable Style/RedundantFetchBlock
80
- expect(result).to eq("new_value")
81
- end
82
- end
83
-
84
- describe "#metrics" do
85
- it "sends a metrics command and returns the metrics" do
86
- payload = { cmd: "metrics" }
87
- response = { ok: true, value: { reads: 10, writes: 5 } }.to_json
88
-
89
- expect(mock_socket).to receive(:puts).with(payload.to_json)
90
- expect(mock_socket).to receive(:gets).and_return(response)
91
-
92
- expect(client.metrics).to eq({ reads: 10, writes: 5 })
93
- end
94
- end
95
-
96
- describe "#reset_metrics!" do
97
- it "sends a reset_metrics command" do
98
- payload = { cmd: "reset_metrics" }
99
- response = { ok: true, value: nil }.to_json
100
-
101
- expect(mock_socket).to receive(:puts).with(payload.to_json)
102
- expect(mock_socket).to receive(:gets).and_return(response)
103
-
104
- expect(client.reset_metrics!).to be_nil
105
- end
106
- end
107
-
108
- describe "#reset!" do
109
- it "sends a reset command" do
110
- payload = { cmd: "reset" }
111
- response = { ok: true, value: nil }.to_json
112
-
113
- expect(mock_socket).to receive(:puts).with(payload.to_json)
114
- expect(mock_socket).to receive(:gets).and_return(response)
115
-
116
- expect(client.reset!).to be_nil
117
- end
118
- end
119
-
120
- describe "error handling" do
121
- it "warns when the socket is missing" do
122
- allow(UNIXSocket).to receive(:open).and_raise(Errno::ENOENT)
123
-
124
- expect { client.read("test_key") }.to output(/Socket missing/).to_stderr
125
- expect(client.read("test_key")).to be_nil
126
- end
127
-
128
- it "raises an error when the server returns an error" do
129
- response = { ok: false, error: "Something went wrong" }.to_json
130
-
131
- expect(mock_socket).to receive(:puts)
132
- expect(mock_socket).to receive(:gets).and_return(response)
133
-
134
- expect { client.read("test_key") }.to raise_error("Something went wrong")
135
- end
136
- end
137
- end
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "spec_helper"
4
+
5
+ RSpec.describe MudisClient do # rubocop:disable Metrics/BlockLength
6
+ let(:socket_path) { MudisIPCConfig::SOCKET_PATH }
7
+ let(:socket_class) { MudisIPCConfig.use_tcp? ? TCPSocket : UNIXSocket }
8
+ let(:mock_socket) { instance_double(socket_class) }
9
+ let(:client) { MudisClient.new }
10
+
11
+ around do |example|
12
+ ClimateControl.modify("SOCKET_PATH" => socket_path) do
13
+ example.run
14
+ end
15
+ end
16
+
17
+ before do
18
+ if MudisIPCConfig.use_tcp?
19
+ allow(TCPSocket).to receive(:new).and_return(mock_socket)
20
+ else
21
+ allow(UNIXSocket).to receive(:open).and_yield(mock_socket)
22
+ end
23
+ allow(mock_socket).to receive(:close)
24
+ end
25
+
26
+ describe "#read" do
27
+ it "sends a read command and returns the value" do
28
+ payload = { cmd: "read", key: "test_key", namespace: nil }
29
+ response = { ok: true, value: "test_value" }.to_json
30
+
31
+ expect(mock_socket).to receive(:puts).with(payload.to_json)
32
+ expect(mock_socket).to receive(:gets).and_return(response)
33
+
34
+ expect(client.read("test_key")).to eq("test_value")
35
+ end
36
+ end
37
+
38
+ describe "#write" do
39
+ it "sends a write command and returns the value" do
40
+ payload = { cmd: "write", key: "test_key", value: "test_value", ttl: nil, namespace: nil }
41
+ response = { ok: true, value: nil }.to_json
42
+
43
+ expect(mock_socket).to receive(:puts).with(payload.to_json)
44
+ expect(mock_socket).to receive(:gets).and_return(response)
45
+
46
+ expect(client.write("test_key", "test_value")).to be_nil
47
+ end
48
+ end
49
+
50
+ describe "#delete" do
51
+ it "sends a delete command and returns the value" do
52
+ payload = { cmd: "delete", key: "test_key", namespace: nil }
53
+ response = { ok: true, value: nil }.to_json
54
+
55
+ expect(mock_socket).to receive(:puts).with(payload.to_json)
56
+ expect(mock_socket).to receive(:gets).and_return(response)
57
+
58
+ expect(client.delete("test_key")).to be_nil
59
+ end
60
+ end
61
+
62
+ describe "#exists?" do
63
+ it "sends an exists command and returns true" do
64
+ payload = { cmd: "exists", key: "test_key", namespace: nil }
65
+ response = { ok: true, value: true }.to_json
66
+
67
+ expect(mock_socket).to receive(:puts).with(payload.to_json)
68
+ expect(mock_socket).to receive(:gets).and_return(response)
69
+
70
+ expect(client.exists?("test_key")).to eq(true)
71
+ end
72
+ end
73
+
74
+ describe "#fetch" do
75
+ it "fetches an existing value or writes a new one" do
76
+ read_response = { ok: true, value: nil }.to_json
77
+ write_payload = { cmd: "write", key: "test_key", value: "new_value", ttl: nil, namespace: nil }
78
+ write_response = { ok: true, value: nil }.to_json
79
+
80
+ expect(mock_socket).to receive(:puts).with({ cmd: "read", key: "test_key", namespace: nil }.to_json)
81
+ expect(mock_socket).to receive(:gets).and_return(read_response)
82
+ expect(mock_socket).to receive(:puts).with(write_payload.to_json)
83
+ expect(mock_socket).to receive(:gets).and_return(write_response)
84
+
85
+ result = client.fetch("test_key") { "new_value" } # rubocop:disable Style/RedundantFetchBlock
86
+ expect(result).to eq("new_value")
87
+ end
88
+ end
89
+
90
+ describe "#metrics" do
91
+ it "sends a metrics command and returns the metrics" do
92
+ payload = { cmd: "metrics" }
93
+ response = { ok: true, value: { reads: 10, writes: 5 } }.to_json
94
+
95
+ expect(mock_socket).to receive(:puts).with(payload.to_json)
96
+ expect(mock_socket).to receive(:gets).and_return(response)
97
+
98
+ expect(client.metrics).to eq({ reads: 10, writes: 5 })
99
+ end
100
+ end
101
+
102
+ describe "#reset_metrics!" do
103
+ it "sends a reset_metrics command" do
104
+ payload = { cmd: "reset_metrics" }
105
+ response = { ok: true, value: nil }.to_json
106
+
107
+ expect(mock_socket).to receive(:puts).with(payload.to_json)
108
+ expect(mock_socket).to receive(:gets).and_return(response)
109
+
110
+ expect(client.reset_metrics!).to be_nil
111
+ end
112
+ end
113
+
114
+ describe "#reset!" do
115
+ it "sends a reset command" do
116
+ payload = { cmd: "reset" }
117
+ response = { ok: true, value: nil }.to_json
118
+
119
+ expect(mock_socket).to receive(:puts).with(payload.to_json)
120
+ expect(mock_socket).to receive(:gets).and_return(response)
121
+
122
+ expect(client.reset!).to be_nil
123
+ end
124
+ end
125
+
126
+ describe "error handling" do
127
+ it "warns when the socket is missing" do
128
+ if MudisIPCConfig.use_tcp?
129
+ allow(TCPSocket).to receive(:new).and_raise(Errno::ECONNREFUSED)
130
+ else
131
+ allow(UNIXSocket).to receive(:open).and_raise(Errno::ENOENT)
132
+ end
133
+
134
+ expect { client.read("test_key") }.to output(/Cannot connect/).to_stderr
135
+ expect(client.read("test_key")).to be_nil
136
+ end
137
+
138
+ it "raises an error when the server returns an error" do
139
+ response = { ok: false, error: "Something went wrong" }.to_json
140
+
141
+ expect(mock_socket).to receive(:puts)
142
+ expect(mock_socket).to receive(:gets).and_return(response)
143
+
144
+ expect { client.read("test_key") }.to raise_error("Something went wrong")
145
+ end
146
+ end
147
+ end
@@ -1,90 +1,100 @@
1
- # frozen_string_literal: true
2
-
3
- require "socket"
4
- require "json"
5
-
6
- require_relative "spec_helper"
7
-
8
- RSpec.describe MudisServer do # rubocop:disable Metrics/BlockLength
9
- let(:socket_path) { MudisServer::SOCKET_PATH }
10
-
11
- before do
12
- allow(Mudis).to receive(:read).and_return("mock_value")
13
- allow(Mudis).to receive(:write)
14
- allow(Mudis).to receive(:delete)
15
- allow(Mudis).to receive(:exists?).and_return(true)
16
- allow(Mudis).to receive(:fetch).and_return("mock_fetched_value")
17
- allow(Mudis).to receive(:metrics).and_return({ reads: 1, writes: 1 })
18
- allow(Mudis).to receive(:reset_metrics!)
19
- allow(Mudis).to receive(:reset!)
20
-
21
- # Start the server in a separate thread
22
- Thread.new { MudisServer.start! }
23
- sleep 0.1 # Allow the server to start
24
- end
25
-
26
- after do
27
- File.unlink(socket_path) if File.exist?(socket_path)
28
- end
29
-
30
- def send_request(request)
31
- UNIXSocket.open(socket_path) do |sock|
32
- sock.puts(JSON.dump(request))
33
- JSON.parse(sock.gets, symbolize_names: true)
34
- end
35
- end
36
-
37
- it "handles the 'read' command" do
38
- response = send_request({ cmd: "read", key: "test_key", namespace: "test_ns" })
39
- expect(response).to eq({ ok: true, value: "mock_value" })
40
- expect(Mudis).to have_received(:read).with("test_key", namespace: "test_ns")
41
- end
42
-
43
- it "handles the 'write' command" do
44
- response = send_request({ cmd: "write", key: "test_key", value: "test_value", ttl: 60, namespace: "test_ns" })
45
- expect(response).to eq({ ok: true })
46
- expect(Mudis).to have_received(:write).with("test_key", "test_value", expires_in: 60, namespace: "test_ns")
47
- end
48
-
49
- it "handles the 'delete' command" do
50
- response = send_request({ cmd: "delete", key: "test_key", namespace: "test_ns" })
51
- expect(response).to eq({ ok: true })
52
- expect(Mudis).to have_received(:delete).with("test_key", namespace: "test_ns")
53
- end
54
-
55
- it "handles the 'exists' command" do
56
- response = send_request({ cmd: "exists", key: "test_key", namespace: "test_ns" })
57
- expect(response).to eq({ ok: true, value: true })
58
- expect(Mudis).to have_received(:exists?).with("test_key", namespace: "test_ns")
59
- end
60
-
61
- it "handles the 'fetch' command" do
62
- response = send_request({ cmd: "fetch", key: "test_key", ttl: 60, namespace: "test_ns",
63
- fallback: "fallback_value" })
64
- expect(response).to eq({ ok: true, value: "mock_fetched_value" })
65
- expect(Mudis).to have_received(:fetch).with("test_key", expires_in: 60, namespace: "test_ns")
66
- end
67
-
68
- it "handles the 'metrics' command" do
69
- response = send_request({ cmd: "metrics" })
70
- expect(response).to eq({ ok: true, value: { reads: 1, writes: 1 } })
71
- expect(Mudis).to have_received(:metrics)
72
- end
73
-
74
- it "handles the 'reset_metrics' command" do
75
- response = send_request({ cmd: "reset_metrics" })
76
- expect(response).to eq({ ok: true })
77
- expect(Mudis).to have_received(:reset_metrics!)
78
- end
79
-
80
- it "handles the 'reset' command" do
81
- response = send_request({ cmd: "reset" })
82
- expect(response).to eq({ ok: true })
83
- expect(Mudis).to have_received(:reset!)
84
- end
85
-
86
- it "handles unknown commands" do
87
- response = send_request({ cmd: "unknown_command" })
88
- expect(response).to eq({ ok: false, error: "unknown command: unknown_command" })
89
- end
90
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "socket"
4
+ require "json"
5
+
6
+ require_relative "spec_helper"
7
+
8
+ RSpec.describe MudisServer do # rubocop:disable Metrics/BlockLength
9
+ let(:socket_path) { MudisIPCConfig::SOCKET_PATH }
10
+
11
+ before(:all) do
12
+ # Start the server once for all tests
13
+ Thread.new { MudisServer.start! }
14
+ sleep 0.2 # Allow the server to start
15
+ end
16
+
17
+ before(:each) do
18
+ allow(Mudis).to receive(:read).and_return("mock_value")
19
+ allow(Mudis).to receive(:write)
20
+ allow(Mudis).to receive(:delete)
21
+ allow(Mudis).to receive(:exists?).and_return(true)
22
+ allow(Mudis).to receive(:fetch).and_return("mock_fetched_value")
23
+ allow(Mudis).to receive(:metrics).and_return({ reads: 1, writes: 1 })
24
+ allow(Mudis).to receive(:reset_metrics!)
25
+ allow(Mudis).to receive(:reset!)
26
+ end
27
+
28
+ after do
29
+ File.unlink(socket_path) if File.exist?(socket_path) && !MudisIPCConfig.use_tcp?
30
+ end
31
+
32
+ def send_request(request)
33
+ if MudisIPCConfig.use_tcp?
34
+ TCPSocket.open(MudisIPCConfig::TCP_HOST, MudisIPCConfig::TCP_PORT) do |sock|
35
+ sock.puts(JSON.dump(request))
36
+ JSON.parse(sock.gets, symbolize_names: true)
37
+ end
38
+ else
39
+ UNIXSocket.open(socket_path) do |sock|
40
+ sock.puts(JSON.dump(request))
41
+ JSON.parse(sock.gets, symbolize_names: true)
42
+ end
43
+ end
44
+ end
45
+
46
+ it "handles the 'read' command" do
47
+ response = send_request({ cmd: "read", key: "test_key", namespace: "test_ns" })
48
+ expect(response).to eq({ ok: true, value: "mock_value" })
49
+ expect(Mudis).to have_received(:read).with("test_key", namespace: "test_ns")
50
+ end
51
+
52
+ it "handles the 'write' command" do
53
+ response = send_request({ cmd: "write", key: "test_key", value: "test_value", ttl: 60, namespace: "test_ns" })
54
+ expect(response).to eq({ ok: true, value: nil })
55
+ expect(Mudis).to have_received(:write).with("test_key", "test_value", expires_in: 60, namespace: "test_ns")
56
+ end
57
+
58
+ it "handles the 'delete' command" do
59
+ response = send_request({ cmd: "delete", key: "test_key", namespace: "test_ns" })
60
+ expect(response).to eq({ ok: true, value: nil })
61
+ expect(Mudis).to have_received(:delete).with("test_key", namespace: "test_ns")
62
+ end
63
+
64
+ it "handles the 'exists' command" do
65
+ response = send_request({ cmd: "exists", key: "test_key", namespace: "test_ns" })
66
+ expect(response).to eq({ ok: true, value: true })
67
+ expect(Mudis).to have_received(:exists?).with("test_key", namespace: "test_ns")
68
+ end
69
+
70
+ it "handles the 'fetch' command" do
71
+ response = send_request({ cmd: "fetch", key: "test_key", ttl: 60, namespace: "test_ns",
72
+ fallback: "fallback_value" })
73
+ expect(response).to eq({ ok: true, value: "mock_fetched_value" })
74
+ expect(Mudis).to have_received(:fetch).with("test_key", expires_in: 60, namespace: "test_ns")
75
+ end
76
+
77
+ it "handles the 'metrics' command" do
78
+ response = send_request({ cmd: "metrics" })
79
+ expect(response).to eq({ ok: true, value: { reads: 1, writes: 1 } })
80
+ expect(Mudis).to have_received(:metrics)
81
+ end
82
+
83
+ it "handles the 'reset_metrics' command" do
84
+ response = send_request({ cmd: "reset_metrics" })
85
+ expect(response).to eq({ ok: true, value: nil })
86
+ expect(Mudis).to have_received(:reset_metrics!)
87
+ end
88
+
89
+ it "handles the 'reset' command" do
90
+ response = send_request({ cmd: "reset" })
91
+ expect(response).to eq({ ok: true, value: nil })
92
+ expect(Mudis).to have_received(:reset!)
93
+ end
94
+
95
+ it "handles unknown commands" do
96
+ response = send_request({ cmd: "unknown_command" })
97
+ expect(response[:ok]).to be false
98
+ expect(response[:error]).to match(/unknown command/i)
99
+ end
100
+ end