Skip to content

Commit

Permalink
merge
Browse files Browse the repository at this point in the history
  • Loading branch information
aspicer committed Nov 7, 2024
2 parents 109f0c4 + d95550f commit b8bc040
Show file tree
Hide file tree
Showing 89 changed files with 2,871 additions and 4,294 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/rust-docker-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,6 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/arm64,linux/amd64
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: BIN=${{ matrix.image }}

- name: Container image digest
Expand Down
32 changes: 11 additions & 21 deletions cypress/e2e/experiments.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,25 +42,6 @@ describe('Experiments', () => {
.type('test-variant-2')
.should('have.value', 'test-variant-2')

// Continue to step 2
cy.get('[data-attr="continue-experiment-creation"]').click()

// Goal type selection is visible
cy.get('[data-attr="experiment-goal-type-select"]')
.should('be.visible')
.within(() => {
cy.contains('Conversion funnel').should('be.visible')
cy.contains('Trend').should('be.visible')
})

// Goal input is visible
cy.get('[data-attr="experiment-goal-input"]')
.should('be.visible')
.within(() => {
cy.get('li.ActionFilterRow').should('exist')
cy.get('button').contains('Add funnel step').should('exist')
})

// Save experiment
cy.get('[data-attr="save-experiment"]').first().click()
})
Expand Down Expand Up @@ -98,10 +79,19 @@ describe('Experiments', () => {
.type('test-variant-2')
.should('have.value', 'test-variant-2')

// Continue creation
cy.get('[data-attr="continue-experiment-creation"]').first().click()
// Save experiment
cy.get('[data-attr="save-experiment"]').first().click()

// Set the experiment goal once the experiment is drafted
cy.get('[data-attr="add-experiment-goal"]').click()

// Wait for the goal modal to open and click the confirmation button
cy.get('.LemonModal__layout').should('be.visible')
cy.contains('Change experiment goal').should('be.visible')
cy.get('.LemonModal__footer').contains('button', 'Save').should('have.attr', 'aria-disabled', 'true')
cy.get('.LemonModal__content').contains('button', 'Add funnel step').click()
cy.get('.LemonModal__footer').contains('button', 'Save').should('not.have.attr', 'aria-disabled', 'true')
cy.get('.LemonModal__footer').contains('button', 'Save').click()
}

it('create, launch and stop experiment with new ui', () => {
Expand Down
1 change: 0 additions & 1 deletion cypress/fixtures/api/decide.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ export function decideResponse(featureFlags) {
},
toolbarParams: {
toolbarVersion: 'toolbar',
jsURL: 'http://localhost:8234/',
},
isAuthenticated: true,
supportedCompression: ['gzip', 'gzip-js', 'lz64'],
Expand Down
16 changes: 12 additions & 4 deletions ee/clickhouse/views/experiments.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,10 @@ def validate_parameters(self, value):
return value

def create(self, validated_data: dict, *args: Any, **kwargs: Any) -> Experiment:
if not validated_data.get("filters"):
raise ValidationError("Filters are required to create an Experiment")
is_draft = "start_date" not in validated_data or validated_data["start_date"] is None

if not validated_data.get("filters") and not is_draft:
raise ValidationError("Filters are required when creating a launched experiment")

saved_metrics_data = validated_data.pop("saved_metrics_ids", [])

Expand All @@ -299,8 +301,6 @@ def create(self, validated_data: dict, *args: Any, **kwargs: Any) -> Experiment:

feature_flag_key = validated_data.pop("get_feature_flag_key")

is_draft = "start_date" not in validated_data or validated_data["start_date"] is None

properties = validated_data["filters"].get("properties", [])

if properties:
Expand Down Expand Up @@ -369,6 +369,14 @@ def create(self, validated_data: dict, *args: Any, **kwargs: Any) -> Experiment:
return experiment

def update(self, instance: Experiment, validated_data: dict, *args: Any, **kwargs: Any) -> Experiment:
if (
not instance.filters.get("events")
and not instance.filters.get("actions")
and validated_data.get("start_date")
and not validated_data.get("filters")
):
raise ValidationError("Filters are required when launching an experiment")

update_saved_metrics = "saved_metrics_ids" in validated_data
saved_metrics_data = validated_data.pop("saved_metrics_ids", []) or []

Expand Down
170 changes: 150 additions & 20 deletions ee/clickhouse/views/test/test_clickhouse_experiments.py
Original file line number Diff line number Diff line change
Expand Up @@ -681,30 +681,13 @@ def test_invalid_create(self):
"end_date": None,
"feature_flag_key": ff_key,
"parameters": {},
"filters": {}, # also invalid
"filters": {},
},
)

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json()["detail"], "This field may not be null.")

ff_key = "a-b-tests"
response = self.client.post(
f"/api/projects/{self.team.id}/experiments/",
{
"name": "None",
"description": "",
"start_date": None,
"end_date": None,
"feature_flag_key": ff_key,
"parameters": {},
"filters": {}, # still invalid
},
)

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json()["detail"], "Filters are required to create an Experiment")

def test_invalid_update(self):
# Draft experiment
ff_key = "a-b-tests"
Expand Down Expand Up @@ -808,7 +791,12 @@ def test_draft_experiment_doesnt_have_FF_active_even_after_updates(self):
# Now update
response = self.client.patch(
f"/api/projects/{self.team.id}/experiments/{id}",
{"description": "Bazinga", "filters": {}},
{
"description": "Bazinga",
"filters": {
"events": [{"id": "$pageview"}],
},
},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)

Expand Down Expand Up @@ -839,7 +827,7 @@ def test_launching_draft_experiment_activates_FF(self):
"end_date": None,
"feature_flag_key": ff_key,
"parameters": {},
"filters": {"events": []},
"filters": {"events": [{"id": "$pageview"}]},
},
)

Expand Down Expand Up @@ -1732,6 +1720,148 @@ def test_create_experiment_updates_feature_flag_cache(self):
},
)

def test_create_draft_experiment_with_filters(self) -> None:
ff_key = "a-b-tests"
response = self.client.post(
f"/api/projects/{self.team.id}/experiments/",
{
"name": "Test Experiment",
"description": "",
"start_date": None,
"end_date": None,
"feature_flag_key": ff_key,
"parameters": None,
"filters": {
"events": [
{"order": 0, "id": "$pageview"},
{"order": 1, "id": "$pageleave"},
],
"properties": [],
},
},
)

self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.json()["name"], "Test Experiment")
self.assertEqual(response.json()["feature_flag_key"], ff_key)

def test_create_launched_experiment_with_filters(self) -> None:
ff_key = "a-b-tests"
response = self.client.post(
f"/api/projects/{self.team.id}/experiments/",
{
"name": "Test Experiment",
"description": "",
"start_date": "2021-12-01T10:23",
"end_date": None,
"feature_flag_key": ff_key,
"parameters": None,
"filters": {
"events": [
{"order": 0, "id": "$pageview"},
{"order": 1, "id": "$pageleave"},
],
"properties": [],
},
},
)

self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.json()["name"], "Test Experiment")
self.assertEqual(response.json()["feature_flag_key"], ff_key)

def test_create_draft_experiment_without_filters(self) -> None:
ff_key = "a-b-tests"
response = self.client.post(
f"/api/projects/{self.team.id}/experiments/",
{
"name": "Test Experiment",
"description": "",
"start_date": None,
"end_date": None,
"feature_flag_key": ff_key,
"parameters": None,
"filters": {},
},
)

self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.json()["name"], "Test Experiment")
self.assertEqual(response.json()["feature_flag_key"], ff_key)

def test_create_launched_experiment_without_filters(self) -> None:
ff_key = "a-b-tests"
response = self.client.post(
f"/api/projects/{self.team.id}/experiments/",
{
"name": "Test Experiment",
"description": "",
"start_date": "2021-12-01T10:23",
"end_date": None,
"feature_flag_key": ff_key,
"parameters": None,
"filters": {},
},
)

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json()["detail"], "Filters are required when creating a launched experiment")

def test_launch_draft_experiment_without_filters(self) -> None:
ff_key = "a-b-tests"
response = self.client.post(
f"/api/projects/{self.team.id}/experiments/",
{
"name": "Test Experiment",
"description": "",
"start_date": None,
"end_date": None,
"feature_flag_key": ff_key,
"parameters": None,
"filters": {},
},
)

self.assertEqual(response.status_code, status.HTTP_201_CREATED)
draft_exp = response.json()

response = self.client.patch(
f"/api/projects/{self.team.id}/experiments/{draft_exp['id']}",
{
"name": "Test Experiment",
"description": "",
"start_date": "2021-12-01T10:23",
"end_date": None,
"feature_flag_key": ff_key,
"parameters": None,
"filters": {},
},
)

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json()["detail"], "Filters are required when launching an experiment")

response = self.client.patch(
f"/api/projects/{self.team.id}/experiments/{draft_exp['id']}",
{
"name": "Test Experiment",
"description": "",
"start_date": "2021-12-01T10:23",
"end_date": None,
"feature_flag_key": ff_key,
"parameters": None,
"filters": {
"events": [
{"order": 0, "id": "$pageview"},
{"order": 1, "id": "$pageleave"},
],
"properties": [],
},
},
)

self.assertEqual(response.status_code, status.HTTP_200_OK)


class TestExperimentAuxiliaryEndpoints(ClickhouseTestMixin, APILicensedTest):
def _generate_experiment(self, start_date="2024-01-01T10:23", extra_parameters=None):
Expand Down
Loading

0 comments on commit b8bc040

Please sign in to comment.