debian-mirror-gitlab/spec/lib/gitlab/json_spec.rb

489 lines
12 KiB
Ruby
Raw Normal View History

2020-04-22 19:07:51 +05:30
# frozen_string_literal: true
require "spec_helper"
2023-01-13 00:05:48 +05:30
# We can disable the cop that enforces the use of this class
# as we need to test around it.
#
# rubocop: disable Gitlab/Json
2020-04-22 19:07:51 +05:30
RSpec.describe Gitlab::Json do
2020-05-24 23:13:21 +05:30
before do
stub_feature_flags(json_wrapper_legacy_mode: true)
end
2021-01-29 00:20:46 +05:30
describe ".parse" do
2022-11-25 23:54:43 +05:30
it "is aliased" do
[:parse!, :load, :decode].each do |method|
expect(described_class.method(method)).to eq(described_class.method(:parse))
end
end
2021-01-29 00:20:46 +05:30
context "legacy_mode is disabled by default" do
it "parses an object" do
expect(subject.parse('{ "foo": "bar" }')).to eq({ "foo" => "bar" })
2020-05-24 23:13:21 +05:30
end
2020-04-22 19:07:51 +05:30
2021-01-29 00:20:46 +05:30
it "parses an array" do
expect(subject.parse('[{ "foo": "bar" }]')).to eq([{ "foo" => "bar" }])
2020-05-24 23:13:21 +05:30
end
2021-01-29 00:20:46 +05:30
it "parses a string" do
expect(subject.parse('"foo"', legacy_mode: false)).to eq("foo")
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
it "parses a true bool" do
expect(subject.parse("true", legacy_mode: false)).to be(true)
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
it "parses a false bool" do
expect(subject.parse("false", legacy_mode: false)).to be(false)
2020-05-24 23:13:21 +05:30
end
2020-04-22 19:07:51 +05:30
end
2021-01-29 00:20:46 +05:30
context "legacy_mode is enabled" do
it "parses an object" do
expect(subject.parse('{ "foo": "bar" }', legacy_mode: true)).to eq({ "foo" => "bar" })
end
2020-05-24 23:13:21 +05:30
2021-01-29 00:20:46 +05:30
it "parses an array" do
expect(subject.parse('[{ "foo": "bar" }]', legacy_mode: true)).to eq([{ "foo" => "bar" }])
end
2020-05-24 23:13:21 +05:30
2021-01-29 00:20:46 +05:30
it "raises an error on a string" do
expect { subject.parse('"foo"', legacy_mode: true) }.to raise_error(JSON::ParserError)
end
2020-05-24 23:13:21 +05:30
2021-01-29 00:20:46 +05:30
it "raises an error on a true bool" do
expect { subject.parse("true", legacy_mode: true) }.to raise_error(JSON::ParserError)
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
it "raises an error on a false bool" do
expect { subject.parse("false", legacy_mode: true) }.to raise_error(JSON::ParserError)
2020-05-24 23:13:21 +05:30
end
2021-01-29 00:20:46 +05:30
end
2020-05-24 23:13:21 +05:30
2021-01-29 00:20:46 +05:30
context "feature flag is disabled" do
before do
stub_feature_flags(json_wrapper_legacy_mode: false)
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
it "parses an object" do
expect(subject.parse('{ "foo": "bar" }', legacy_mode: true)).to eq({ "foo" => "bar" })
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
it "parses an array" do
expect(subject.parse('[{ "foo": "bar" }]', legacy_mode: true)).to eq([{ "foo" => "bar" }])
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
it "parses a string" do
expect(subject.parse('"foo"', legacy_mode: true)).to eq("foo")
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
it "parses a true bool" do
expect(subject.parse("true", legacy_mode: true)).to be(true)
2020-05-24 23:13:21 +05:30
end
2021-01-29 00:20:46 +05:30
it "parses a false bool" do
expect(subject.parse("false", legacy_mode: true)).to be(false)
end
end
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
describe ".parse!" do
context "legacy_mode is disabled by default" do
it "parses an object" do
expect(subject.parse!('{ "foo": "bar" }')).to eq({ "foo" => "bar" })
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
it "parses an array" do
expect(subject.parse!('[{ "foo": "bar" }]')).to eq([{ "foo" => "bar" }])
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
it "parses a string" do
expect(subject.parse!('"foo"', legacy_mode: false)).to eq("foo")
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
it "parses a true bool" do
expect(subject.parse!("true", legacy_mode: false)).to be(true)
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
it "parses a false bool" do
expect(subject.parse!("false", legacy_mode: false)).to be(false)
2020-05-24 23:13:21 +05:30
end
2020-04-22 19:07:51 +05:30
end
2021-01-29 00:20:46 +05:30
context "legacy_mode is enabled" do
it "parses an object" do
expect(subject.parse!('{ "foo": "bar" }', legacy_mode: true)).to eq({ "foo" => "bar" })
2020-05-24 23:13:21 +05:30
end
2020-04-22 19:07:51 +05:30
2021-01-29 00:20:46 +05:30
it "parses an array" do
expect(subject.parse!('[{ "foo": "bar" }]', legacy_mode: true)).to eq([{ "foo" => "bar" }])
2020-05-24 23:13:21 +05:30
end
2021-01-29 00:20:46 +05:30
it "raises an error on a string" do
expect { subject.parse!('"foo"', legacy_mode: true) }.to raise_error(JSON::ParserError)
2020-05-24 23:13:21 +05:30
end
2020-04-22 19:07:51 +05:30
2021-01-29 00:20:46 +05:30
it "raises an error on a true bool" do
expect { subject.parse!("true", legacy_mode: true) }.to raise_error(JSON::ParserError)
2020-05-24 23:13:21 +05:30
end
2021-01-29 00:20:46 +05:30
it "raises an error on a false bool" do
expect { subject.parse!("false", legacy_mode: true) }.to raise_error(JSON::ParserError)
2020-05-24 23:13:21 +05:30
end
2020-04-22 19:07:51 +05:30
end
2021-01-29 00:20:46 +05:30
context "feature flag is disabled" do
before do
stub_feature_flags(json_wrapper_legacy_mode: false)
2020-05-24 23:13:21 +05:30
end
2021-01-29 00:20:46 +05:30
it "parses an object" do
expect(subject.parse!('{ "foo": "bar" }', legacy_mode: true)).to eq({ "foo" => "bar" })
end
2020-05-24 23:13:21 +05:30
2021-01-29 00:20:46 +05:30
it "parses an array" do
expect(subject.parse!('[{ "foo": "bar" }]', legacy_mode: true)).to eq([{ "foo" => "bar" }])
2020-05-24 23:13:21 +05:30
end
2021-01-29 00:20:46 +05:30
it "parses a string" do
expect(subject.parse!('"foo"', legacy_mode: true)).to eq("foo")
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
it "parses a true bool" do
expect(subject.parse!("true", legacy_mode: true)).to be(true)
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
it "parses a false bool" do
expect(subject.parse!("false", legacy_mode: true)).to be(false)
2020-05-24 23:13:21 +05:30
end
2020-07-28 23:09:34 +05:30
end
2021-01-29 00:20:46 +05:30
end
2020-05-24 23:13:21 +05:30
2021-01-29 00:20:46 +05:30
describe ".dump" do
it "dumps an object" do
expect(subject.dump({ "foo" => "bar" })).to eq('{"foo":"bar"}')
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
it "dumps an array" do
expect(subject.dump([{ "foo" => "bar" }])).to eq('[{"foo":"bar"}]')
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
it "dumps a string" do
expect(subject.dump("foo")).to eq('"foo"')
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
it "dumps a true bool" do
expect(subject.dump(true)).to eq("true")
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
it "dumps a false bool" do
expect(subject.dump(false)).to eq("false")
end
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
describe ".generate" do
let(:obj) do
{ test: true, "foo.bar" => "baz", is_json: 1, some: [1, 2, 3] }
2020-04-22 19:07:51 +05:30
end
2022-11-25 23:54:43 +05:30
it "is aliased" do
expect(described_class.method(:encode)).to eq(described_class.method(:generate))
end
2021-01-29 00:20:46 +05:30
it "generates JSON" do
expected_string = <<~STR.chomp
{"test":true,"foo.bar":"baz","is_json":1,"some":[1,2,3]}
STR
2020-05-24 23:13:21 +05:30
2021-01-29 00:20:46 +05:30
expect(subject.generate(obj)).to eq(expected_string)
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
it "allows you to customise the output" do
opts = {
indent: " ",
space: " ",
space_before: " ",
object_nl: "\n",
array_nl: "\n"
}
2020-05-24 23:13:21 +05:30
2021-01-29 00:20:46 +05:30
json = subject.generate(obj, opts)
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
expected_string = <<~STR.chomp
{
"test" : true,
"foo.bar" : "baz",
"is_json" : 1,
"some" : [
1,
2,
3
]
}
STR
expect(json).to eq(expected_string)
2020-07-28 23:09:34 +05:30
end
2021-01-29 00:20:46 +05:30
end
2020-05-24 23:13:21 +05:30
2021-01-29 00:20:46 +05:30
describe ".pretty_generate" do
let(:obj) do
{
test: true,
"foo.bar" => "baz",
is_json: 1,
some: [1, 2, 3],
more: { test: true },
multi_line_empty_array: [],
multi_line_empty_obj: {}
}
end
2020-05-24 23:13:21 +05:30
2021-01-29 00:20:46 +05:30
it "generates pretty JSON" do
expected_string = <<~STR.chomp
{
"test": true,
"foo.bar": "baz",
"is_json": 1,
"some": [
1,
2,
3
],
"more": {
"test": true
},
"multi_line_empty_array": [
],
"multi_line_empty_obj": {
}
}
STR
2020-05-24 23:13:21 +05:30
2021-01-29 00:20:46 +05:30
expect(subject.pretty_generate(obj)).to eq(expected_string)
end
it "allows you to customise the output" do
opts = {
space_before: " "
}
json = subject.pretty_generate(obj, opts)
expected_string = <<~STR.chomp
{
"test" : true,
"foo.bar" : "baz",
"is_json" : 1,
"some" : [
1,
2,
3
],
"more" : {
"test" : true
},
"multi_line_empty_array" : [
],
"multi_line_empty_obj" : {
}
}
STR
expect(json).to eq(expected_string)
2020-04-22 19:07:51 +05:30
end
end
2021-01-29 00:20:46 +05:30
context "the feature table is missing" do
2020-07-28 23:09:34 +05:30
before do
2021-01-29 00:20:46 +05:30
allow(Feature::FlipperFeature).to receive(:table_exists?).and_return(false)
2020-04-22 19:07:51 +05:30
end
2021-01-29 00:20:46 +05:30
it "skips legacy mode handling" do
2022-07-16 23:28:13 +05:30
expect(Feature).not_to receive(:enabled?).with(:json_wrapper_legacy_mode)
2020-04-22 19:07:51 +05:30
2021-01-29 00:20:46 +05:30
subject.send(:handle_legacy_mode!, {})
2020-04-22 19:07:51 +05:30
end
2020-07-28 23:09:34 +05:30
end
2020-04-22 19:07:51 +05:30
2021-01-29 00:20:46 +05:30
context "the database is missing" do
2020-07-28 23:09:34 +05:30
before do
2021-01-29 00:20:46 +05:30
allow(Feature::FlipperFeature).to receive(:table_exists?).and_raise(PG::ConnectionBad)
2020-04-22 19:07:51 +05:30
end
2021-01-29 00:20:46 +05:30
it "still parses json" do
expect(subject.parse("{}")).to eq({})
end
2020-07-28 23:09:34 +05:30
2021-01-29 00:20:46 +05:30
it "still generates json" do
expect(subject.dump({})).to eq("{}")
2020-04-22 19:07:51 +05:30
end
end
2020-07-28 23:09:34 +05:30
describe Gitlab::Json::GrapeFormatter do
subject { described_class.call(obj, env) }
let(:obj) { { test: true } }
let(:env) { {} }
let(:result) { "{\"test\":true}" }
2022-04-04 11:22:00 +05:30
it "generates JSON" do
expect(subject).to eq(result)
2020-04-22 19:07:51 +05:30
end
2022-04-04 11:22:00 +05:30
it "uses Gitlab::Json" do
expect(Gitlab::Json).to receive(:dump).with(obj)
2020-04-22 19:07:51 +05:30
2022-04-04 11:22:00 +05:30
subject
2020-04-22 19:07:51 +05:30
end
2021-04-29 21:17:54 +05:30
context "precompiled JSON" do
let(:obj) { Gitlab::Json::PrecompiledJson.new(result) }
it "renders the string directly" do
expect(subject).to eq(result)
end
it "calls #to_s on the object" do
expect(obj).to receive(:to_s).once
subject
end
it "doesn't run the JSON formatter" do
expect(Gitlab::Json).not_to receive(:dump)
subject
end
end
end
describe Gitlab::Json::PrecompiledJson do
subject(:precompiled) { described_class.new(obj) }
describe "#to_s" do
subject { precompiled.to_s }
context "obj is a string" do
let(:obj) { "{}" }
it "returns a string" do
expect(subject).to eq("{}")
end
end
context "obj is an array" do
let(:obj) { ["{\"foo\": \"bar\"}", "{}"] }
it "returns a string" do
expect(subject).to eq("[{\"foo\": \"bar\"},{}]")
end
end
context "obj is an array of un-stringables" do
let(:obj) { [BasicObject.new] }
it "raises an error" do
expect { subject }.to raise_error(NoMethodError)
end
end
context "obj is something else" do
let(:obj) { {} }
it "raises an error" do
expect { subject }.to raise_error(described_class::UnsupportedFormatError)
end
end
end
2020-04-22 19:07:51 +05:30
end
2020-10-24 23:57:45 +05:30
describe Gitlab::Json::LimitedEncoder do
2021-09-04 01:27:46 +05:30
subject { described_class.encode(obj, limit: 10.kilobytes) }
2020-10-24 23:57:45 +05:30
context 'when object size is acceptable' do
let(:obj) { { test: true } }
it 'returns json string' do
is_expected.to eq("{\"test\":true}")
end
end
context 'when object is too big' do
let(:obj) { [{ test: true }] * 1000 }
it 'raises LimitExceeded error' do
expect { subject }.to raise_error(
Gitlab::Json::LimitedEncoder::LimitExceeded
)
end
end
2021-09-04 01:27:46 +05:30
context 'when object contains ASCII-8BIT encoding' do
let(:obj) { [{ a: "\x8F" }] * 1000 }
it 'does not raise encoding error' do
expect { subject }.not_to raise_error
expect(subject).to be_a(String)
expect(subject.size).to eq(10001)
end
end
2020-10-24 23:57:45 +05:30
end
2023-01-13 00:05:48 +05:30
describe Gitlab::Json::RailsEncoder do
let(:obj) do
{ foo: "<span>bar</span>" }
end
it "is used by ActiveSupport::JSON" do
expect_next_instance_of(described_class) do |encoder|
expect(encoder).to receive(:encode).with(obj)
end
ActiveSupport::JSON.encode(obj)
end
it "is used by .to_json calls" do
expect_next_instance_of(described_class) do |encoder|
expect(encoder).to receive(:encode).with(obj)
end
obj.to_json
end
it "is consistent with the original JSON implementation" do
default_encoder = ActiveSupport::JSON::Encoding::JSONGemEncoder
original_result = ActiveSupport::JSON::Encoding.use_encoder(default_encoder) do
ActiveSupport::JSON.encode(obj)
end
new_result = ActiveSupport::JSON::Encoding.use_encoder(described_class) do
ActiveSupport::JSON.encode(obj)
end
expect(new_result).to eq(original_result)
end
it "behaves the same when processing invalid unicode data" do
invalid_obj = { test: "Gr\x80\x81e" }
default_encoder = ActiveSupport::JSON::Encoding::JSONGemEncoder
original_result = ActiveSupport::JSON::Encoding.use_encoder(default_encoder) do
expect { ActiveSupport::JSON.encode(invalid_obj) }.to raise_error(JSON::GeneratorError)
end
new_result = ActiveSupport::JSON::Encoding.use_encoder(described_class) do
expect { ActiveSupport::JSON.encode(invalid_obj) }.to raise_error(JSON::GeneratorError)
end
expect(new_result).to eq(original_result)
end
end
2020-04-22 19:07:51 +05:30
end
2023-01-13 00:05:48 +05:30
# rubocop: enable Gitlab/Json