Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions spec/integration/graphql/link_expansion/reverse_link_inclusion_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
RSpec.describe "reverse link expansion inclusion" do
def for_content_store(target_edition, link_type:, with_drafts:)
reverse_link_type = ExpansionRules::REVERSE_LINKS[link_type.to_sym] || link_type
Presenters::Queries::ExpandedLinkSet
.by_edition(target_edition, with_drafts:)
.links
.fetch(reverse_link_type, [])
end

def for_graphql(target_edition, link_type:, with_drafts:)
GraphQL::Dataloader.with_dataloading do |dataloader|
request = dataloader.with(
Sources::ReverseLinkedToEditionsSource,
content_store: with_drafts ? "draft" : "live",
locale: target_edition.locale,
).request([target_edition, link_type])

request.load
end
end

test_cases = GraphqlLinkExpansionInclusionHelpers::ReverseLinks::TestCaseFactory.all

test_cases.each do |test_case|
context "when the link kind is #{test_case.link_kind}" do
context test_case.with_drafts_description do
context test_case.target_edition_locale_description do
it test_case.description do
linked_edition = create(
:live_edition,
document: create(
:document,
locale: test_case.root_locale,
),
)

source_edition = create(
:edition,
state: test_case.state,
content_store: test_case.state == "draft" ? "draft" : "live",
document_type: test_case.source_edition_document_type,
document: create(
:document,
locale: test_case.locale,
),
test_case.link_kind => [
{
link_type: test_case.link_type,
target_content_id: linked_edition.content_id,
},
],
)

if test_case.state == "unpublished"
create(
:unpublishing,
edition: source_edition,
type: test_case.source_edition_unpublishing_type,
)
end

%w[content_store graphql].each do |destination|
# GraphQL doesn't yet support drafts
next if destination == "graphql" && test_case.with_drafts?

# GraphQL will only attempt to find a reverse link for a field that is declared
# as a reverse_links_field in app/graphql/types/edition_type.rb. This test
# calls the dataloader directly, but the dataloader won't check that the
# link type is reversible, so we should skip irreversible link types.
next if destination == "graphql" && !test_case.allowed_reverse_link_type

result = send(
:"for_#{destination}",
linked_edition,
**{
link_type: test_case.link_type,
with_drafts: test_case.with_drafts?,
},
)

if test_case.included
# if result.size != 1
# byebug
# end
expect(result.size).to(
eq(1),
"unexpected exclusion for #{destination}",
)
else
expect(result).to(
be_empty,
"unexpected inclusion for #{destination}",
)
end
end
end
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
module GraphqlLinkExpansionInclusionHelpers
module ReverseLinks
class TestCase
def initialize(
with_drafts:,
root_locale:,
state:,
renderable_document_type:,
locale:,
withdrawal:,
permitted_unpublished_link_type:,
allowed_reverse_link_type:,
link_kind:
)
@with_drafts = with_drafts
@root_locale = root_locale
@state = state
@renderable_document_type = renderable_document_type
@locale = locale
@withdrawal = withdrawal
@permitted_unpublished_link_type = permitted_unpublished_link_type
@allowed_reverse_link_type = allowed_reverse_link_type
@link_kind = link_kind
end

attr_reader :root_locale, :state, :renderable_document_type, :locale, :allowed_reverse_link_type, :link_kind

def with_drafts?
@with_drafts
end

def description
inclusion_string = included ? "includes" : "excludes"
state_string = if state == "unpublished"
link_type_description = permitted_unpublished_link_type? ? "permitted" : "unpermitted"
unpublishing_type_description = withdrawal? ? "withdrawal" : "non-withdrawal"
"an unpublished #{unpublishing_type_description} with a #{link_type_description} link type"
else
state
end
locale_string = locale == "default" ? "in the default locale" : "in the locale \"#{locale}\""
document_type_string = "a #{renderable_document_type ? 'renderable' : 'non-renderable'} document type"
link_reversibility_string = "via a #{allowed_reverse_link_type ? 'reversible' : 'non-reversible'} link type (i.e. #{link_type})"

"#{inclusion_string} a source edition that is #{[state_string, document_type_string, locale_string, link_reversibility_string].to_sentence}"
end

def with_drafts_description
"when #{with_drafts? ? 'accepting' : 'rejecting'} drafts"
end

def target_edition_locale_description
"when the target edition's locale is \"#{root_locale}\""
end

def link_kind_description
"when the link kind is \"#{link_kind}\""
end

def source_edition_document_type
@source_edition_document_type ||= if renderable_document_type
renderable_types = GovukSchemas::DocumentTypes.valid_document_types - Edition::NON_RENDERABLE_FORMATS
renderable_types.sample
else
Edition::NON_RENDERABLE_FORMATS.sample
end
end

def link_type
@link_type ||= begin
reverse_link_types = ExpansionRules::REVERSE_LINKS.keys.map(&:to_s)
permitted_unpublished_link_types = Link::PERMITTED_UNPUBLISHED_LINK_TYPES

link_types ||= []

link_types.concat(reverse_link_types & permitted_unpublished_link_types) if allowed_reverse_link_type && permitted_unpublished_link_type?
link_types.concat(reverse_link_types - permitted_unpublished_link_types) if allowed_reverse_link_type && !permitted_unpublished_link_type?
link_types.concat(permitted_unpublished_link_types - reverse_link_types) if !allowed_reverse_link_type && permitted_unpublished_link_type?
link_types.concat(%w[ordered_related_items]) if !allowed_reverse_link_type && !permitted_unpublished_link_type?

link_types.sample
end
end

def source_edition_unpublishing_type
@source_edition_unpublishing_type ||= begin
return "withdrawal" if withdrawal?

Unpublishing::VALID_TYPES.reject { it == "withdrawal" }.sample
end
end

def included
renderable_document_type &&
allowed_reverse_link_type &&
(with_drafts? || state != "draft") &&
(state != "unpublished" ||
(withdrawal? && permitted_unpublished_link_type?)
) &&
(link_kind == "link_set_links" && [root_locale, Edition::DEFAULT_LOCALE].include?(locale) || link_kind == "edition_links" && [root_locale].include?(locale))
end

private

def withdrawal?
@withdrawal
end

def permitted_unpublished_link_type?
@permitted_unpublished_link_type
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
module GraphqlLinkExpansionInclusionHelpers
module ReverseLinks
class TestCaseFactory
class << self
def all
with_drafts_values = [true, false]
root_locale_values = [Edition::DEFAULT_LOCALE, "fr"]

state_values = %w[published unpublished draft]
withdrawal_values = [true, false, nil]
permitted_unpublished_link_type_values = [true, false, nil]

renderable_document_type_values = [false, true]
locale_values = [Edition::DEFAULT_LOCALE, "fr", "hu"]
allowed_reverse_link_type_values = [true, false]
link_kind_values = %w[link_set_links edition_links]

with_drafts_values.product(
root_locale_values,
state_values,
withdrawal_values,
permitted_unpublished_link_type_values,
renderable_document_type_values,
locale_values,
allowed_reverse_link_type_values,
link_kind_values,
).map {
{
with_drafts: _1,
root_locale: _2,
state: _3,
withdrawal: _4,
permitted_unpublished_link_type: _5,
renderable_document_type: _6,
locale: _7,
allowed_reverse_link_type: _8,
link_kind: _9,
}
}
.reject { |test_case|
[
redundant_locale(test_case),
invalid_state(test_case),
].any?
}
.map { TestCase.new(**it) }
end

private

def redundant_locale(test_case)
test_case[:root_locale] == Edition::DEFAULT_LOCALE &&
test_case[:locale] == "hu"
end

def invalid_state(test_case)
if test_case[:state] == "unpublished"
test_case[:withdrawal].nil? || test_case[:permitted_unpublished_link_type].nil?
else
!test_case[:withdrawal].nil? || !test_case[:permitted_unpublished_link_type].nil?
end
end
end
end
end
end