* Adding conditional spam handling for user profile
Prior to this commit, we always checked the user's name for spam
matching criteria. With this change, we're allowing a site
administrator to toggle on a feature flag for more rigorous profile
checking.
The reason for the increased rigor is that there could be unexpected
consequences (e.g. marking a user as spam who was previously not caught
as a spammer). If you want the feature, enable the
`:more_rigorous_user_profile_spam_checking` flag.
Closesforem/forem#18157
Related to forem/forem-internal-eng#453
* Adding additional test case
* Update spec/services/spam/handler_spec.rb
Co-authored-by: Fernando Valverde <fernando@visualcosita.com>
Co-authored-by: Fernando Valverde <fernando@visualcosita.com>
Prior to this commit, we may have left lingering podcast entries that
required manual cleanup. As a bonus, I also added deleting podcasts
when we delete users.
Closesforem/forem#18041
In addition I adjusted rubocop to not complain about the require method call
```
spec/services/users/delete_spec.rb:1:1: C: \
Style/TopLevelMethodDefinition: \
Do not define methods at the top-level. \
(https://rubystyle.guide#top-level-methods)
require "rails_helper"
^^^^^^^^^^^^^^^^^^^^^^
```
* Article query spec for scheduled articles
* Added scheduled article badge on the user dashboard
* Added published_at field to editor options
* Accept and validate published_at from editor
* Refactor published_at validation
* Allow 1-minute difference in published_at
* Notice on an unpublished article page
* Added specs for 'Click to edit' link on scheduled article preview page
* ContextNotification model
* Articles::Publish worker
* Added specs for articles publish worker
* Schedule publish articles worker
* Added tests to check for scheduled posts in feeds
* Don't allow managing scheduled articles
* Don't send notifications for scheduled articles
* Set published_at in Articles::Updater when publishing
* Published_at value in post options
* Pass timezone and set published_at accordingly
* Limit setting published_at to the future
* Readonly published_at for articles that were already published
* Chagning published_at format in editor v1 (start)
* Changed published_at format in frontmatter, specs
* Added specs for updating published_at from frontmatter
* Fixed accepting past published_at for articles published_from_feed
* Enabled published_at validation: don't allow updating published_at for already published articles
* Validate published_at on create
* Added a spec for updating published_at for exported articles
* Fixed specs related to creating articles with past published_at
* Fixed specs related to past published_at for articles
* Added a hack so that admins would be able to update published_at
* Switch button text schedule/publish when changin publishedAt
* Fixed saving published_at with timezone
* Added a feature flag for scheduling articles
* Default text in markdown editor depends on feature flag
* Enable article editor cache again
* Fixed the default value in the markdown editor
* Fix sitemaps spec
* Removed tooltip
* Fixed articles update specs
* Added missing locales
* Fixed article create specs
* Fixed spec
* Removed commented code
* Returned enabling extensions in the schema
* Returned accidentally deleted constraint
* Make articles query spec more stable
Co-authored-by: Jeremy Friesen <jeremy.n.friesen@gmail.com>
* Removed commented code
* Removed unused code
* A clearer policy
Co-authored-by: Jeremy Friesen <jeremy.n.friesen@gmail.com>
* Use StringInquirer for article current state
* Added a note and todo to articles factory past trait
* Remove duplicated PropType
Co-authored-by: Suzanne Aitchison <suzanne@forem.com>
* Refactor query in the Articles::PublishWorker
Co-authored-by: Mac Siri <krairit.siri@gmail.com>
* Refactor articleForm.jsx
Co-authored-by: Mac Siri <krairit.siri@gmail.com>
* Removed specs that are no longer relevant
* Removed useless onKeyUp on a hidden input
* Refactored articleForm
* Hide scheduling from post options when published_at is readonly
* Run sends notifications worker every 5 minutes instead of every minute
Co-authored-by: Jeremy Friesen <jeremy.n.friesen@gmail.com>
Co-authored-by: Suzanne Aitchison <suzanne@forem.com>
Co-authored-by: Mac Siri <krairit.siri@gmail.com>
* Move .skip_confirmation! call to .find_or_create_user! method
* Add regression tests to avoid email confirmation delivery
* Keep the original place where we had user.skip_confirmation! too
* Update logic to require email confirmation from omniauth
* test confirmation is required with SMTP
* Keep :notice instead of :global_notice
* Forem Account bypass email confirmation reorg
* inline comment reorder + clarification
* Apply suggestions from code review
Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>
Co-authored-by: Fernando Valverde <fernando@visualcosita.com>
Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>
* Add tooltip for cover image button
* Add cover img tooltip to v1 editor
* Use a slightly different message
* Fix tests
* Use proper comment syntax for front matter
* Fix Cypress test
* Use regex to find matching label
* added ability to assign moderator role via admin/super admin
* create Mod User in seeds
* add Cypress specs
* fix failing Cypress tests
* fix failing specs by moving test Mod user to the end
* nudge Travis
* nudge Travis
Co-authored-by: Arit Amana <msarit@gmail.com>
* Fixing variant query specs
As I was working on adding a new variant, I discovered that not all of
our variants were being tested. This had to do with a faulty assumption
regarding the scoping of the `variant` local variable.
The change ensures that we declare a subject once for each defined
variant within their own `describe` block. Without that `describe`
block, you end up repurposing the same subject.
I found this because I made a deliberate error in the upcoming variant
and checked the tests. They passed.
Related to forem/forem#17584
* Update spec/services/articles/feeds/variant_query_spec.rb
Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>
* Update spec/services/articles/feeds/variant_query_spec.rb
Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>
Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>
* First greedy algorithm approach to awarding community wellness badge
* Update app/services/badges/award_community_wellness.rb
I'm sorry I ignored you rubocop. It's still on draft for a reason 😅
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Update app/services/badges/award_community_wellness.rb
Okay, whatever you say rubocop. This line will go away before merging anyways
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Use app/queries/comments/community_wellness_query.rb to replace greedy algorithm
* Query indenting + rename positive_reactions to negative_reactions in EXCEPT
* First step towards query spec
* Fix spec and make it more robust
* Slight cleanup/tweaks
* Badge reward & message
* copy edits + spec improvements + 3 days ago min for query
* Cron update to run daily + small tweak for FeatureFlag use
* inline comment tweaks
* Slight reorder of guard checks in service (FeatureFlag related)
* PR review feedback
* Fix specs from static method refactor
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Don't create podcast episodes without a media_url
There is an early return from add_media_url to prevent using an empty
enclosure_url, which avoids errors in
`GetMediaUrl.call(a_blank_value)`, however there's also a not null
constraint on podcast_episodes.media_url in the database, which causes
upsert to raise an error.
Returning early here avoids the error (which had halted the worker).
See also
https://app.honeybadger.io/projects/66984/faults/80833402
This error happens regularly, 96 times per day in DEV, it looks like at least
one podcast feed_url is for an rss feed filtered by /tag/podcast/,
(and at least one post was tagged "podcast" but didn't include any
media to get).
My understanding is at worst we'll ignore the error silently.
* Add test cases
given a blog post (not a podcast episode, no media url), don't raise
any error, don't create any episode.
Included codepunk.io feed that was showing this issue initially.
You had a good ride, but your successor, the
`Articles::Feeds::VariantQuery`, is doing well in production. And has
been for quite awhile.
You helped us get to a better spot in regards to the feed algorithm, but
your time has come to rest as a memory in our git history.
* Rename unsubscribe to remove_from_mailchimp_newsletters
* Permanently delete from mailchimp instead of unsubscribing
* Rename method and remove comment
* Don't use i18n because of error message
We don't want to use i18n here because e.title comes from the error response. If a Forem
was using a different locale and Mailchimp for newsletters, they would still receive the
error response in English (because of Mailchimp).
* Refactor and rename a bit
* Rename methods in tests accordingly
* Remove now moot test
* Rename method in other places it was previously called
* Remove unused translation, see 62fee247ee2565e4d3d14919a8636a76ce5ecc9f
* Fix typo and report error if not 404
This commit does three things:
1. Breaks the inheritance of the VariantQuery.
2. Revisits the in-line documentation to better reflect current state.
3. Removes branching logic that we don't need.
My goal is to have a VariantQuery that is what we need, conforms to the
method interface of WeightedQueryStrategy but does not rely on it.
After we merge this, we can observe if things break on DEV and fallback
to WeightedQueryStrategy if this doesn't work. Once the
`Articles::Feeds::VariantQuery` has fledged on it's own, we can remove
the feature flag and then finally remove the weighted query strategy.
But that's the future steps.
Related to forem/forem#17393
* Allowing VariantQuery for Feed Generation
Apologies for the breadth of this pull request, I had considered many
small commits, but felt that would've been more effort for the value
provided.
This commit includes the following:
- Documentation updates to the feed variant (though not the final pass)
- Renaming and adding RelevancyLevers that help differentiate
- Adding RelevancyLever#range to provide documentation
- Reducing redundent controller logic by making a
`Articles::Feeds.feed_for` method.
- Adding some configuration validation for RelevancyLevers
- Adding constants for better clarification
- Testing unhappy paths for feed configuration
- Adjusting the module namespace of some objects
- Exposing top-level configurations for variants (along with their
defaults)
- Creating the VariantyQuery that at present inherits from the
`Articles::Feeds::WeightedQueryStrategy`
As implemented, we can deploy this code to production without using the
new VariantQuery. Once we toggle on the
`:feed_uses_variant_query_feature` FeatureFlag, it will switch to using
the VariantQuery. The VariantQuery's two variants and the
internal configuration of `Articles::Feeds::WeightedQueryStrategy`
produce the same query.
The goal of this factor is to allow for a quick on and off toggle of the
feed query; to ensure that what we introduce remains performant.
- Closesforem/forem#17272
- Closesforem/forem#17276
- Closesforem/forem#17216
In addition, I will be recording a code-walkthrough and linking that
recording to the pull request.
* Apply suggestions from code review
Co-authored-by: Mac Siri <krairit.siri@gmail.com>
Co-authored-by: Mac Siri <krairit.siri@gmail.com>
* ContextNotification model
* Create and destroy context notifications when notifications are sent/unsent
* Added a transaction when creating notifications and context notifications
* Documentation for context notifications
* Update article builder spec
In #16536 the tests were updated to match a change in the
logic (rather than "needs authorization" the response was "store
location", and we checked for those values).
In #17294 we re-adjusted the meaning of the second returned value from
Articles::Builder.call, and the call sites now label the second value
as "needs_authorization" again.
Restore the same name to the call sites in the spec, and simplify the
example descriptions (this might be a mistake).
* Restore long example descriptions
This restores the names of the test cases to the original.
There are two problems engrained in this:
1. The undocumented expectation of `/new` needed to other sites linking
with prefill information.
2. The prefill parameters being lost during the authentication process.
The first bug was introduced as part forem/forem#16536. The second is
likely introduced as part of a Rails upgrade (namely `request.path` most
likely once included the query parameters but with our current
implementation it does not).
Closesforem/forem#17292
* Disable flaky test cases
These two tests (counting the number of items returned by the service
in the past week) depend on synchronizing the database time with the
process time, and fail when zonebie is active and the current timezone
is more than 12 hours ahead of UTC ("Samoa" for example fails).
* Use UTC times when calculating the periods for overview charts
The database will use UTC to calculate DATE() for grouping, and
ensuring the same time used when filtering avoids issues where the
ruby/server time and the database time are in distinct timezones, and
posts are published on different days in the two instances.
* Remove unused test-side date helper
* Revert "Use UTC times when calculating the periods overview charts"
This just moved the problem to the request specs. Since the spec is
still failing it's better to get the test suppressed while a fix is
determined
This reverts commit cbad78efcee9df51fd4f0a5214d00358c5253167.
* Use sluggerize to produce a slug from podcast episode title
This is essentially the process we use for Article#title_to_slug
without the random trailing bits on the end (since podcast episodes
had not worked that way previously).
* Add test case for slug generation
* Oh, the test only works if you checkin the fixture, too
* Expect to raise error should use a block
This is kind of pedantic, since we _had_ been using a lambda, and
calling as a value. Now explicitly call the proc in a block and expect
the block to raise error.
* Replace lambda with block
Replace lambda passed as a value to an implicit block with a block.
* Disable current period specs
These are failing about half of the time (when the test runs east of
UTC, like Perth or Astana, the counts are incorrect, when the test
runs west of UTC like America/Chicago or Alaska, it passes).
* Cheat by setting clock to noon UTC
This aligns article published dates in ruby with those calculated by
postgresql. Copy the decision made in
https://github.com/forem/forem/pull/3518 for the analytics service.
* Remove testing comments
I had added titles to some of the articles I wanted excluded from the
result to confirm they were being included.
* transliterate when generating attribute names to avoid emptiness
If you enter a non-ascii (non `\w` matching) string as the profile
field label, the attribute name is the empty string.
This causes problems outlined in #16391
To avoid losing user provided data, transliterate (Sterile is the same
tool we're using for Article#title_to_slug) before matching against
the word regex.
If we persist an empty string, or persist a non-`\w` name, the
coordinating regex in Profile::ATTRIBUTE_NAME_REGEX will leave a nil
match and raise NoMethodError (the method missing is for
match[:attribute_name] when match was nil, not Profile - that's the
next commit.
* Guard against nil matches
If an attribute name doesn't match the regex, the match is nil, and
trying to access (nil)[:attribute_name] raises a NoMethodError.
If there was no match, assume profile does not respond to the
selector, and don't handle it in method missing.
* Ensure generated attribute name is valid before saving
This raises a validation error if the generated name (from the label)
would be empty.
It's not an optimal error message (since the user can't see the
internal attribute name) but it prevents persisting broken/empty data
* actually raise error when validating
validate/valid? only return true or false (and set errors on the
object). In order to reject the creation, we need to raise a
validation error, not only call validate. I think this is because the
execution of before_create hooks happens after validation (which is
why the validation was only checked on update, not create).
* Generate an attribute name completely independent of the field label
This prevents mistakenly labeling a field "Class" or "Association" or
one of the other hundred public methods an AR model like Profile
exposes. Since attribute_name will be passed to `profile.public_send`
we really shouldn't build selectors from user supplied inputs.
* Use the admin supplied label in the sidebar
The profile decorator is used in the Users#show page to populate the
sidebar fields. Don't use the attribute name (which we mangled during
creation, and now generate randomly) as the label, use the label.
* Make the label lookup null safe, and filter attributes more
* Update data update script to not expect predictable labels
This is low impact since it ran in july, but we no longer know what
attribute name a label will create.
* Fix moderator spec
"Test Field" label no longer predictably generates :test_field as an
attribute name. Ask the field what it's name is before asking profile
about it.
* Fix profile preview card request spec
Remove the assumption that profile responds to a method name based on
the label for Work and Education fields.
* Update old DUS and its test
This can probably be archived
* Update profile spec to use fields generated attribute names
We used to "know" how attributes were generated from labels. Now we don't.
* update e2e seeds for profile field change
* Don't expect attribute name to be based on the label
* expect created profile fields respond to their attribute name
* Update system test
The label, not the attribute name, is shown on the profile form (the
field has an id related to the attribute name, but the view shows the
mutable/human-readable label).
* Keep the field title lowercase when sending the json preview card
The userMetadata component expects "work" and "education" to be
attributes of the metadata, but the ui_attributes_for() method was
titlizing these (for display).
Ideally we wouldn't have "special purposed" these two field names, but
they're there.
* update profile field by attribute name, not based on label
* Update profile field removal assumptions
We don't know what the method selectors will be, we have to ask.
* remove old test
* Update translations for Education and Work
Since the ui_attributes_for(area:) now gives the label, not the
attribute, we need to match the label of the profile field.
Note to self: this exposes an issue in localizing the custom profile
fields (probably a bigger problem for large, international communities
like DEV than some others, but trying to match static translation
files against user-modifiable database records seems like a problem
we'll see again).
* Empty commit to retrigger buildkite
* Remove profile field migration update scripts
Cloned the specs from the other "remove unused scripts" script.
* Remove unused scripts
The data update script removes the entry from the table (recording
that these have run) - we also want to remove the files (preventing
them from running again).
* remove unneeded spec for removed file
* when translation for header area field not found, use the title
Only Work and Education already have keys in the yml translation file,
and there's not a great (or easy?) way to make multiple translations
on these fields right now.
Since an admin can create a new field, and assign it to the header
area, we can't assume the code has a configured translation key for
this field.
Fallback to the title (we do this in another context already) if
there's no translation.
* PR feedback: Avoid n+1 query for labels
the original implementation of "label_for_attribute" had an n+1 query
looping over each matched key.
Follow suggested improvement and pull labels and attributes at once
from the db and modify the returned hash.
* Downcase title before looking for translation key
This avoids putting "odd" capitalized keys into the yml translation
file
Revert addition of "Work" and "Education" to the users files.
* use a let binding for duplicated test data
* Update app/models/profile_field.rb
prefer SecureRandom.hex for a dashless uuid (instead of removing the dashes).
Co-authored-by: Jamie Gaskins <jgaskins@hey.com>
Co-authored-by: Jamie Gaskins <jgaskins@hey.com>
* Award badges for github commits with various milestones
* Fixes on award badges to github commits
* Add data update script to add badges
* Disable award_multi_commit_contributors
* Revert "Disable award_multi_commit_contributors"
This reverts commit 1852f34fd45bd23b716cf80ebbd9ce2c1879a819.
* Remove DUS
* Change badge name
* Only use award_contributors
* Simplify specs
* Revert "Simplify specs"
This reverts commit 230514ccea68445b057991d3d536fbb2a590a3b1.
* Revert "Only use award_contributors"
This reverts commit a92735a079f43610aedc805f74f032466d23f1cf.
Co-authored-by: Mac Siri <krairit.siri@gmail.com>
* prefer case to multiple if branches
case klass works fine on inheritance chains (we don't need to match on
inheritance explicitly).
* Verify case on error class behaves as before
* Fix tests
I don't know how I committed tests that were failing, but don't do that
We had been (since the beginning) disabling webmock in this
test (which means vcr was _not_ intercepting requests and we were
making them live). This appears to have been a work-around for an
issue where the captured URL in the cassette pointed to cloudinary,
while the code was fetching from the source directly, and an unhandled
request error was raised when running the spec.
However, the upstream source (https://via.placeholder.com/350x150)
activated cloudflare bot detection, and rather than a png image
response, we were getting an unprocessable html gateway page "Checking
your browser..." from cloudflare, and the test was failing.
Point the vcr cassette at the same url the test checks, and remove the
local webmock disable.
* Don't suggest users for an anonymous visitor to follow
The sidebar raises an error when rendering suggested users for a tag
when the user is signed out.
https://app.honeybadger.io/projects/66984/faults/79342962/
* Assert empty response returned for anonymous requests
* Only fetch RSS of users who can create articles
This commit leverages changes from forem/forem#16732 and
forem/forem#16763 to ensure that we're only fetching RSS feeds for
folks who have permission to create articles.
In addition, I chose to rename the variable `user_scope` to
`users_scope` to bring consistency in the variable names.
I also chose to refactor a private method so that it wasn't setting two
instance variables but was instead returning a value that could be used
to set an instance variable.
Closesforem/forem#16487
* Amending method name
This change makes it easier to resolveforem/forem#16487.
What do I mean by that? To address forem/forem#16487, I need to only
select user's who are authorized to create articles. In
forem/forem#16732 I added a method that will allow chaining of a User
scope. So with this current commit and forem/forem#16732, I'm
triangulating on an approach that will make that change easier.
I did not want to conflate those two, as mixing this PR's change and
what is necessary for the closing PR would create a more complicated
review. Not unduly complicated, but one that will require more tests
and a change in logic. And for someone reviewing the diff, those
concerns could easily be lost.
Yes, I have changed the method signature, but that method signature is
limited to one location:
```shell
❯ rg "Feeds::Import.call"
app/workers/feeds/import_articles_worker.rb
21: ::Feeds::Import.call(users_scope: users_scope, earlier_than: earlier_than)
```
So I look to the method signature a bit as an internal API, hence the change.
Related to forem/forem#16487
* don't set nil when size not called
recently, #16570 worked around an issue where aggregated siblings
wasn't present, by only calling size if present.
Unfortunately, this causes a comparison of integer (new json_data
aggregated siblings count) with nil (result of the safe navigation for dig()&.size
assigned nil to previous_siblings_size, when we expected it to be
0). The initial error on the same data moved farther down the controller.
If old_json_data is present, but does not have an array in
reaction.aggregated_siblings, we want to have previous size be zero.
Remove guard clause and previous assignment
The issue was not that old_json_data was nil (it came from an existing
notification) but that there were missing keys (or rather that the
json data for some notifications didn't have a reaction key at all).
Remove the guard clause and either set to the size, or zero if there
is none. I don't understand what the guard clause was for, so replace the
safety by adding a nil safe call to dig. I don't _believe_ this is
possible but in case it was we can shorten the check here.
* Add test to cover missing json_data['reaction'] key
* Remove unneeded temporary variable
* Require profile fields to belong to a group
Remove the empty group dropdown from the group select tag
Remove the optional: true from the belongs_to validation (raise a
validation error on save if the group is not provided)
Update test to remove belongs_to's optional setting
* Update ProfileFields::Add spec to include profile field group
* update factory to ensure a non-empty profile field group
* Add required profile field group to seeded profile fields
Since profile field is required, create was failing (silently).
Add required field so these are available in e2e tests.
* Use factory `association` to create a profile field group
https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#explicit-definition
* provide required profile field group when posting data
* Fix one spec, drop another
Both of these scripts will be removed in the other branch (changing
the attribute name) - dropping the test here seems lower trouble than
modifying an out of data data update script to pass.
* skip test slated for removal
Similar to the last commit - this test exercises a script about to be
archived in the other branch.
* Removing Articles::Builder making policy decision
This change is a refactoring through triangulation. Given that
`ArticlePolicy#new?` returned true, I'm prepared to assume that calling
`authorize(Article)` in all cases is acceptable.
So to narrow the Builder making a policy decision I renamed the returned
value to reflect what it was actually doing in the logic. And in
renaming, flipped the polarity of the boolean. Why the flip? Because
`needs_authorization == !store_location`.
In consultation with Allison and Jennie, I'm proceeding with a short-cut
to get me unstuck. That unstuck is namely "I need to ensure that the
articles#new action can go through authorization."
Given that I'll be spending time in the authorization layer, I hope
these noted short-cuts and comments will be useful in future spelunking
efforts regarding authorization.
Closes#16529
* Update app/policies/article_policy.rb
Co-authored-by: Michael Kohl <me@citizen428.net>
* Disabling spec
* Update app/policies/article_policy.rb
Co-authored-by: Dwight Scott <dwight@forem.com>
* Update app/policies/article_policy.rb
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Michael Kohl <me@citizen428.net>
Co-authored-by: Dwight Scott <dwight@forem.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This is the least effort I can presently think of to allow for us to
reprocess user article's and handle the scenario in which the user may
have once had permission to the liquid tag but no longer.
This DI is not something I imagine using, but may highlight a better
approach for liquid tag permissions.
Related to #12146 and #16460
* figuring things out
* working both ways for issues PRs except disc and PRreview
* working on repo urls
* complete implementation and check specs
* resolve Travis failures
* resolving travis failures
* Fix VCR cassette
* fix regexs
Co-authored-by: Jamie Gaskins <jgaskins@hey.com>
Prior to this commit, we had a single test for `FeatureFlag.enabled?`,
namely that it delegates to `Flipper.enabled?`. This commit adds some
tests to both verify behavior and communicate to developers the
intentions of the related methods:
- `FeatureFlag.enabled?`
- `FeatureFlag.accessible?`
My hope is the documentation and tests will help address the horrors
mentioned in conversation in #16416:
> I have lived the horrors of having a feature flag accidently
> flipped or incorrectly setup in the first place and half baked
> functionality going live.
Note: I'm not proposing that we test other `Flipper` methods, but
instead to ensure that we're testing and documenting the behavior of
what I believe to be the two primary mechanisms of "putting something
behind a feature flag."
Related to #16406, #16416