discourse-ai/spec/lib/utils/diff_utils/simple_diff_spec.rb

204 lines
4.8 KiB
Ruby

# frozen_string_literal: true
RSpec.describe DiscourseAi::Utils::DiffUtils::SimpleDiff do
subject { described_class }
describe ".apply" do
it "raises error for nil inputs" do
expect { subject.apply(nil, "search", "replace") }.to raise_error(ArgumentError)
expect { subject.apply("content", nil, "replace") }.to raise_error(ArgumentError)
expect { subject.apply("content", "search", nil) }.to raise_error(ArgumentError)
end
it "prioritizes exact matches over all fuzzy matches" do
content = <<~TEXT
line 1
line 1
lin 1
TEXT
search = " line 1"
replace = " new_line"
expected = <<~TEXT
line 1
new_line
lin 1
TEXT
expect(subject.apply(content, search, replace).strip).to eq(expected.strip)
end
it "raises error when no match is found" do
content = "line1\ncompletely_different\nline3"
search = "nothing_like_this"
replace = "new_line"
expect { subject.apply(content, search, replace) }.to raise_error(
DiscourseAi::Utils::DiffUtils::SimpleDiff::NoMatchError,
)
end
it "replaces all matching occurrences" do
content = "line1\nline2\nmiddle\nline2\nend"
search = "line2"
replace = "new_line2"
expect(subject.apply(content, search, replace)).to eq(
"line1\nnew_line2\nmiddle\nnew_line2\nend",
)
end
it "replaces exact matches" do
content = "line1\nline2\nline3"
search = "line2"
replace = "new_line2"
expect(subject.apply(content, search, replace)).to eq("line1\nnew_line2\nline3")
end
it "handles multi-line replacements" do
content = "start\nline1\nline2\nend"
search = "line1\nline2"
replace = "new_line"
expect(subject.apply(content, search, replace)).to eq("start\nnew_line\nend")
end
it "is forgiving of whitespace differences" do
content = "line1\n line2\nline3"
search = "line2"
replace = "new_line2"
expect(subject.apply(content, search, replace).strip).to eq("line1\n new_line2\nline3")
end
it "is forgiving of small character differences" do
content = "line one one one\nlin2\nline three three" # Notice 'lin2' instead of 'line2'
search = "line2"
replace = "new_line2"
expect(subject.apply(content, search, replace)).to eq(
"line one one one\nnew_line2\nline three three",
)
end
it "is forgiving in multi-line blocks with indentation differences" do
content = "def method\n line1\n line2\nend"
search = "line1\nline2"
replace = "new_content"
expect(subject.apply(content, search, replace)).to eq("def method\nnew_content\nend")
end
it "handles CSS blocks in different orders" do
content = <<~CSS
.first {
color: red;
padding: 10px;
}
.second {
color: blue;
margin: 20px;
}
CSS
search = <<~CSS
.second {
color: blue;
margin: 20px;
}
.first {
color: red;
padding: 10px;
}
CSS
replace = <<~CSS
.new-block {
color: green;
}
CSS
expected = <<~CSS
.new-block {
color: green;
}
CSS
expect(subject.apply(content, search, replace)).to eq(expected.strip)
end
it "handles partial line matches" do
content = "abc hello efg\nabc hello efg"
search = "hello"
replace = "bob"
expect(subject.apply(content, search, replace)).to eq("abc bob efg\nabc bob efg")
end
it "handles JavaScript blocks in different orders" do
content = <<~JS
function first() {
const x = 1;
return x + 2;
}
function second() {
if (true) {
return 42;
}
return 0;
}
JS
search = <<~JS
function second() {
if (true) {
return 42;
}
return 0;
}
function first() {
const x = 1;
return x + 2;
}
JS
replace = <<~JS
function replacement() {
return 'new';
}
JS
expected = <<~JS
function replacement() {
return 'new';
}
JS
expect(subject.apply(content, search, replace).strip).to eq(expected.strip)
end
it "handles missing lines in search" do
original = <<~TEXT
line1
line2
line3
line4
line5
line1
line2
TEXT
search = <<~TEXT
line1
...
line3
...
line1
TEXT
replace = ""
expected = <<~TEXT
line2
TEXT
expect(subject.apply(original, search, replace).strip).to eq(expected.strip)
end
end
end