diff --git a/.coveragerc b/.coveragerc index c135d532..1177471d 100644 --- a/.coveragerc +++ b/.coveragerc @@ -2,4 +2,4 @@ include = scripts/* [report] -fail_under = 80 +fail_under = 85 diff --git a/requests/test_benchmark_business_happy_path.json b/requests/test_benchmark_business_happy_path.json index 45837e9c..91edcbd8 100644 --- a/requests/test_benchmark_business_happy_path.json +++ b/requests/test_benchmark_business_happy_path.json @@ -73,7 +73,7 @@ "method": "POST", "url": "/questionnaire/block-383/", "data": { - "answer-439": "Hi eQ team \ud83d\ude0d" + "answer-439": "Hi eQ Team" } }, { @@ -119,8 +119,8 @@ "method": "POST", "url": "/questionnaire/people/add-person/", "data": { - "first-name": "Erling", - "last-name": "Haaland" + "first-name": "Kylian", + "last-name": "Mbappé" } }, { @@ -540,6 +540,10 @@ "method": "GET", "url": "/submitted/view-response/" }, + { + "method": "GET", + "url": "/submitted/download-pdf" + }, { "method": "GET", "url": "/submitted/thank-you/" diff --git a/scripts/benchmark_stats.py b/scripts/benchmark_stats.py index c60e44c1..0cd27524 100644 --- a/scripts/benchmark_stats.py +++ b/scripts/benchmark_stats.py @@ -20,25 +20,41 @@ def __init__(self, folder_paths: List[str]): "POST": {"response_times": [], "total": 0}, } + self._session_percentile: List[int] = [] + self._pdf_percentile: List[int] = [] self._total_failures: int = 0 self._percentiles: Mapping[int : List[float]] = defaultdict(list) # noqa: E203 self._process_file_data() - def __str__(self): - formatted_percentiles = "\n".join( + self._formatted_percentiles: str = "\n".join( f"{percentile}th: {self.percentiles[percentile]}ms" for percentile in self.PERCENTILES_TO_REPORT ) + + self.formatted_average_get: str = self.formatted_percentile(self.average_get) + self.formatted_average_post: str = self.formatted_percentile(self.average_post) + self.formatted_average_pdf_percentile: str = self.formatted_percentile( + self.average_pdf_percentile + ) + self.formatted_average_session_percentile: str = self.formatted_percentile( + self.average_session_percentile + ) + + def __str__(self): if self.output_to_github: - formatted_percentiles = formatted_percentiles.replace(os.linesep, "
") + formatted_percentiles = self._formatted_percentiles.replace( + os.linesep, "
" + ) return ( f'{{"body": "' f"**Benchmark Results**

" f"Percentile Averages:
" f"{formatted_percentiles}
" - f"GETs (99th): {self.average_get}ms
" - f"POSTs (99th): {self.average_post}ms

" + f"GETs (99th): {self.formatted_average_get}
" + f"POSTs (99th): {self.formatted_average_post}

" + f"PDF: {self.formatted_average_pdf_percentile}
" + f"Session: {self.formatted_average_session_percentile}

" f"Total Requests: {self.total_requests:,}
" f"Total Failures: {self._total_failures:,}
" f'Error Percentage: {(round(self.error_percentage, 2))}%
"}}' @@ -46,10 +62,13 @@ def __str__(self): return ( f"---\n" f"Percentile Averages:\n" - f"{formatted_percentiles}\n" + f"{self._formatted_percentiles}\n" + f"---\n" + f"GETs (99th): {self.formatted_average_get}\n" + f"POSTs (99th): {self.formatted_average_post}\n" f"---\n" - f"GETs (99th): {self.average_get}ms\n" - f"POSTs (99th): {self.average_post}ms\n" + f"PDF: {self.formatted_average_pdf_percentile}\n" + f"Session: {self.formatted_average_session_percentile}\n" f"---\n" f"Total Requests: {self.total_requests:,}\n" f"Total Failures: {self._total_failures:,}\n" @@ -60,34 +79,48 @@ def _process_file_data(self): for file in self.files: with open(file) as fp: for row in DictReader(fp, delimiter=","): - request_count = int( - row.get("Request Count") or row.get("# requests") - ) - if row["Name"] == "Aggregated": - failure_count = row.get("Failure Count") or row.get( - "# failures" - ) - self._total_failures += int(failure_count) - else: - weighted_request_count = self._get_weighted_request_count( - request_count - ) - for percentile in self.PERCENTILES_TO_REPORT: - weighted_percentile = ( - float(row[f"{percentile}%"]) * weighted_request_count - ) - self._percentiles[percentile].append(weighted_percentile) - - percentile_response_time = float( - row[f"{self.PERCENTILE_TO_USE_FOR_AVERAGES}%"] - ) - weighted_response_time = ( - percentile_response_time * weighted_request_count - ) - self._requests[row["Type"]]["response_times"].append( - weighted_response_time - ) - self._requests[row["Type"]]["total"] += request_count + self._process_row(row) + + def _process_row(self, row): + # Handle special 'Aggregated' row + if row["Name"] == "Aggregated": + failure_count = row["Failure Count"] or row["# failures"] + self._total_failures += int(failure_count) + return + + if row["Name"] == "/submitted/download-pdf": + self._pdf_percentile.append( + int(row[f"{self.PERCENTILE_TO_USE_FOR_AVERAGES}%"]) + ) + elif row["Name"] == "/session": + self._session_percentile.append( + int(row[f"{self.PERCENTILE_TO_USE_FOR_AVERAGES}%"]) + ) + + request_count = int(row.get("Request Count") or row.get("# requests")) + weighted_request_count = self._get_weighted_request_count(request_count) + + for percentile in self.PERCENTILES_TO_REPORT: + weighted_percentile = float(row[f"{percentile}%"]) * weighted_request_count + self._percentiles[percentile].append(weighted_percentile) + + percentile_response_time = float(row[f"{self.PERCENTILE_TO_USE_FOR_AVERAGES}%"]) + weighted_response_time = percentile_response_time * weighted_request_count + self._requests[row["Type"]]["response_times"].append(weighted_response_time) + self._requests[row["Type"]]["total"] += request_count + + @property + def average_pdf_percentile(self) -> int | None: + if self._pdf_percentile: + average_pdf_percentile = sum(self._pdf_percentile) / len( + self._pdf_percentile + ) + return round(average_pdf_percentile) + return None + + @property + def average_session_percentile(self) -> int: + return round(sum(self._session_percentile) / len(self._session_percentile)) @property def files(self) -> List[str]: @@ -96,7 +129,7 @@ def files(self) -> List[str]: @property def percentiles(self) -> Mapping: return { - percentile: int( + percentile: round( sum(values) / self._get_weighted_request_count(self.total_requests) ) for percentile, values in self._percentiles.items() @@ -108,14 +141,14 @@ def total_requests(self) -> int: @property def average_get(self) -> int: - return int( + return round( sum(self._requests["GET"]["response_times"]) / self._get_weighted_request_count(self._requests["GET"]["total"]) ) @property def average_post(self) -> int: - return int( + return round( sum(self._requests["POST"]["response_times"]) / self._get_weighted_request_count(self._requests["POST"]["total"]) ) @@ -124,5 +157,9 @@ def average_post(self) -> int: def error_percentage(self) -> float: return (self._total_failures * 100) / self.total_requests + @staticmethod + def formatted_percentile(percentile) -> str: + return f"{percentile}ms" if percentile else "N/A" + def _get_weighted_request_count(self, request_count: int) -> float: return request_count * self.PERCENTILE_TO_USE_FOR_AVERAGES / 100 diff --git a/scripts/visualise_results.py b/scripts/visualise_results.py index 6dc589bc..5d01bdf7 100644 --- a/scripts/visualise_results.py +++ b/scripts/visualise_results.py @@ -6,38 +6,69 @@ from scripts.get_summary import get_results, parse_environment_variables PERCENTILES_TO_GRAPH = (50, 90, 95, 99) +PERCENTILES_TO_PLOT = ("50th", "90th", "95th", "99th") + +ADDITIONAL_METRICS_TO_GRAPH = ("PDF", "Session") class GraphGenerationFailed(Exception): pass -def plot_data(df, number_of_days_to_plot): - try: - plt.style.use("fast") +def plot_data(dataframes, number_of_days_to_plot): + plt.style.use("fast") + fig, axes = plt.subplots(nrows=1, ncols=len(dataframes), figsize=(15, 6)) + for i, dataframe in enumerate(dataframes): + """ + We use the .subplot() method below to switch the indexes of the plots themselves, + if we do not switch plots or remove this method, the visuals (such as the graph background) + and, most importantly, the axes values will only be applied to the second subplot. + """ + plt.subplot(1, len(dataframes), i + 1) if ( number_of_days_to_plot and number_of_days_to_plot <= 45 ): # To make the chart still easily digestible - df.plot.line(marker="o", markersize=8) + dataframe.plot.line( + marker="o", markersize=8, ax=axes[i] if len(dataframes) > 1 else axes + ) plt.grid(True, axis="both", alpha=0.3) else: - df.plot.line() + dataframe.plot.line(ax=axes[i] if len(dataframes) > 1 else axes) plt.margins(0.03, 0.07) - plt.legend(frameon=True, prop={"size": 17}) - plt.xticks(df.index, df["DATE"], size="small", rotation=90) + plt.legend(frameon=True, prop={"size": 10}) + plt.xticks(dataframe.index, dataframe["DATE"], size="small", rotation=90) plt.yticks(size="small") plt.ylabel("Average Response Time (ms)") plt.xlabel("Run Date (YYYY-MM-DD)", labelpad=13) - plt.savefig("performance_graph.png", bbox_inches="tight") - print("Graph saved as performance_graph.png") + +def create_graph(dataframes, number_of_days_to_plot, filename): + try: + plot_data(dataframes, number_of_days_to_plot) + plt.savefig(filename, bbox_inches="tight") + print("Graph saved as", filename) except Exception as e: raise GraphGenerationFailed from e -def get_data_frame(results): +def get_dataframes(folders, number_of_days): + results = list(get_results(folders, number_of_days)) + performance_dataframe = get_performance_data_frame(results) + additional_metrics_dataframe = get_additional_metrics_data_frame(results) + + return [performance_dataframe, additional_metrics_dataframe] + + +def create_dataframe(result_fields, values_to_plot): + return DataFrame( + result_fields, + columns=["DATE", *(f"{percentile}" for percentile in values_to_plot)], + ) + + +def get_performance_data_frame(results): result_fields = [ [ result.date, @@ -49,16 +80,26 @@ def get_data_frame(results): for result in results ] - percentile_columns = (f"{percentile}th" for percentile in PERCENTILES_TO_GRAPH) - return DataFrame(result_fields, columns=["DATE", *percentile_columns]) + return create_dataframe(result_fields, PERCENTILES_TO_PLOT) + + +def get_additional_metrics_data_frame(results): + result_fields = [ + [ + result.date, + result.statistics.average_pdf_percentile, + result.statistics.average_session_percentile, + ] + for result in results + ] + + return create_dataframe(result_fields, ADDITIONAL_METRICS_TO_GRAPH) if __name__ == "__main__": parsed_variables = parse_environment_variables() number_of_days = parsed_variables["number_of_days"] - folders = sorted(glob(f"{parsed_variables['output_dir']}/*")) - results = get_results(folders, number_of_days) - dataframe = get_data_frame(results) - plot_data(dataframe, number_of_days) + dataframes = get_dataframes(folders, number_of_days) + create_graph(dataframes, number_of_days, "performance_graph.png") diff --git a/tests/conftest.py b/tests/conftest.py index d5950d67..83f6f53d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,42 +6,93 @@ "---\n" "Percentile Averages:\n" "50th: 58ms\n" - "90th: 96ms\n" - "95th: 173ms\n" + "90th: 97ms\n" + "95th: 174ms\n" "99th: 301ms\n" "99.9th: 477ms\n" "---\n" "GETs (99th): 380ms\n" - "POSTs (99th): 211ms\n" + "POSTs (99th): 212ms\n" + "---\n" + "PDF: N/A\n" + "Session: 7600ms\n" "---\n" "Total Requests: 70,640\n" "Total Failures: 1\n" "Error Percentage: 0.0%\n" ) +EXPECTED_OUTPUT_SINGLE_FOLDER_WITH_PDF = ( + "---\n" + "Percentile Averages:\n" + "50th: 52ms\n" + "90th: 75ms\n" + "95th: 76ms\n" + "99th: 76ms\n" + "99.9th: 76ms\n" + "---\n" + "GETs (99th): 118ms\n" + "POSTs (99th): 28ms\n" + "---\n" + "PDF: 4700ms\n" + "Session: 180ms\n" + "---\n" + "Total Requests: 1,097\n" + "Total Failures: 0\n" + "Error Percentage: 0.0%\n" +) + EXPECTED_OUTPUT_MULTIPLE_FOLDERS = ( "---\n" "Percentile Averages:\n" "50th: 58ms\n" - "90th: 98ms\n" + "90th: 99ms\n" "95th: 176ms\n" - "99th: 313ms\n" + "99th: 314ms\n" "99.9th: 595ms\n" "---\n" "GETs (99th): 383ms\n" "POSTs (99th): 234ms\n" "---\n" + "PDF: N/A\n" + "Session: 7300ms\n" + "---\n" "Total Requests: 211,841\n" "Total Failures: 2\n" "Error Percentage: 0.0%\n" ) +EXPECTED_OUTPUT_MULTIPLE_FOLDERS_WITH_PDF = ( + "---\n" + "Percentile Averages:\n" + "50th: 58ms\n" + "90th: 98ms\n" + "95th: 175ms\n" + "99th: 311ms\n" + "99.9th: 590ms\n" + "---\n" + "GETs (99th): 380ms\n" + "POSTs (99th): 232ms\n" + "---\n" + "PDF: 4850ms\n" + "Session: 4504ms\n" + "---\n" + "Total Requests: 214,092\n" + "Total Failures: 2\n" + "Error Percentage: 0.0%\n" +) + @pytest.fixture def get_results_single_file(): return get_results(folders=["./tests/mock_stats/2024-02-07T03:09:41"]) +@pytest.fixture +def get_results_single_file_with_pdf_endpoint(): + return get_results(folders=["./tests/mock_stats/2024-02-09T03:09:41"]) + + @pytest.fixture def get_results_single_file_github(): return get_results(folders=["./tests/mock_stats/2024-02-07T03:09:41"]) diff --git a/tests/mock_stats/2024-02-09T03:09:41/mock_output_stats.csv b/tests/mock_stats/2024-02-09T03:09:41/mock_output_stats.csv new file mode 100644 index 00000000..8bd3b260 --- /dev/null +++ b/tests/mock_stats/2024-02-09T03:09:41/mock_output_stats.csv @@ -0,0 +1,96 @@ +Type,Name,Request Count,Failure Count,Median Response Time,Average Response Time,Min Response Time,Max Response Time,Average Content Size,Requests/s,Failures/s,50%,66%,75%,80%,90%,95%,98%,99%,99.9%,99.99%,100% +GET,/questionnaire,10,0,12,12.521058297716081,10.754290968179703,15.16284397803247,255.0,0.16827041003026288,0.0,12,13,14,15,15,15,15,15,15,15,15 +GET,/questionnaire/,20,0,28,28.886581800179556,26.16768190637231,35.674995044246316,124766.5,0.33654082006052577,0.0,29,29,30,30,33,36,36,36,36,36,36 +POST,/questionnaire/,20,0,16,67.88471359177493,11.075941030867398,127.49155692290515,250.0,0.33654082006052577,0.0,120,120,120,120,130,130,130,130,130,130,130 +GET,/questionnaire/any-businesses-or-branches/,10,0,29,29.739139915909618,25.784903089515865,37.286181934177876,104244.0,0.16827041003026288,0.0,29,31,31,32,37,37,37,37,37,37,37 +POST,/questionnaire/any-businesses-or-branches/,10,0,18,19.040479708928615,16.26271998975426,24.36625398695469,273.0,0.16827041003026288,0.0,19,20,21,21,24,24,24,24,24,24,24 +GET,/questionnaire/any-companies-or-branches/,10,0,28,29.01079428847879,26.22722997330129,34.41495297010988,104222.0,0.16827041003026288,0.0,28,29,31,32,34,34,34,34,34,34,34 +POST,/questionnaire/any-companies-or-branches/,10,0,17,17.971195501741022,15.619804034940898,21.72014396637678,331.0,0.16827041003026288,0.0,17,18,18,21,22,22,22,22,22,22,22 +GET,/questionnaire/any-other-companies-or-branches/,10,0,28,29.08987768460065,26.61975403316319,36.18324000854045,106951.0,0.16827041003026288,0.0,28,29,30,30,36,36,36,36,36,36,36 +POST,/questionnaire/any-other-companies-or-branches/,10,0,17,17.7094150101766,15.995775000192225,20.150010008364916,269.0,0.16827041003026288,0.0,17,19,19,19,20,20,20,20,20,20,20 +GET,/questionnaire/any-other-trading-details/,10,0,29,28.194183227606118,25.597497005946934,31.18219505995512,103072.0,0.16827041003026288,0.0,29,29,29,30,31,31,31,31,31,31,31 +POST,/questionnaire/any-other-trading-details/,10,0,19,18.429460003972054,15.40442300029099,22.981974994763732,271.0,0.16827041003026288,0.0,19,19,20,21,23,23,23,23,23,23,23 +GET,/questionnaire/block-379/,10,0,28,30.026265396736562,26.597855030559003,34.05541297979653,103881.0,0.16827041003026288,0.0,31,31,31,34,34,34,34,34,34,34,34 +POST,/questionnaire/block-379/,10,0,17,18.071466695982963,15.901401988230646,24.334451067261398,237.0,0.16827041003026288,0.0,18,19,19,20,24,24,24,24,24,24,24 +GET,/questionnaire/block-381/,10,0,29,29.40658861771226,27.395667042583227,33.33715605549514,107705.0,0.16827041003026288,0.0,29,29,30,32,33,33,33,33,33,33,33 +POST,/questionnaire/block-381/,10,0,18,18.5218526981771,16.332408995367587,22.22067303955555,239.0,0.16827041003026288,0.0,18,18,19,21,22,22,22,22,22,22,22 +GET,/questionnaire/block-383/,10,0,27,27.59334621950984,24.56335793249309,34.02357897721231,103527.0,0.16827041003026288,0.0,27,27,29,32,34,34,34,34,34,34,34 +POST,/questionnaire/block-383/,10,0,16,16.820050589740276,14.71883396152407,22.712663048878312,277.0,0.16827041003026288,0.0,16,16,18,20,23,23,23,23,23,23,23 +GET,/questionnaire/block-4616/,10,0,27,28.000582312233746,25.313068996183574,34.9235039902851,103887.0,0.16827041003026288,0.0,27,27,30,30,35,35,35,35,35,35,35 +POST,/questionnaire/block-4616/,10,0,16,17.99908650573343,15.175107982940972,26.206664042547345,239.0,0.16827041003026288,0.0,18,19,19,20,26,26,26,26,26,26,26 +GET,/questionnaire/block-4952/,10,0,28,34.04230660526082,26.330921100452542,84.26787203643471,104489.0,0.16827041003026288,0.0,29,30,31,32,84,84,84,84,84,84,84 +POST,/questionnaire/block-4952/,10,0,16,16.96460577659309,15.445746947079897,19.629093003459275,239.0,0.16827041003026288,0.0,17,17,18,19,20,20,20,20,20,20,20 +GET,/questionnaire/block-4953/,10,0,29,31.004790775477886,27.259176946245134,47.45357600040734,106833.0,0.16827041003026288,0.0,29,29,30,35,47,47,47,47,47,47,47 +POST,/questionnaire/block-4953/,10,0,17,19.734478509053588,15.719956019893289,29.1489859810099,237.0,0.16827041003026288,0.0,17,18,23,27,29,29,29,29,29,29,29 +GET,/questionnaire/companies/add-company/?previous=any-companies-or-branches,10,0,27,27.450701699126512,25.360413012094796,33.92333001829684,103154.0,0.16827041003026288,0.0,27,27,27,29,34,34,34,34,34,34,34 +POST,/questionnaire/companies/add-company/?previous=any-companies-or-branches,10,0,16,16.604762407951057,15.842041000723839,18.9445250434801,307.0,0.16827041003026288,0.0,16,17,17,17,19,19,19,19,19,19,19 +GET,/questionnaire/companies/{id}/companies-repeating-block-1/,10,0,30,29.946386290248483,28.417859924957156,31.86855895910412,105200.0,0.16827041003026288,0.0,30,30,30,31,32,32,32,32,32,32,32 +POST,/questionnaire/companies/{id}/companies-repeating-block-1/,10,0,17,17.949695198331028,16.497505945153534,20.655443891882896,307.0,0.16827041003026288,0.0,18,18,19,19,21,21,21,21,21,21,21 +GET,/questionnaire/companies/{id}/companies-repeating-block-2/,10,0,28,29.13420789409429,26.86727698892355,33.08491606730968,106197.0,0.16827041003026288,0.0,29,29,30,31,33,33,33,33,33,33,33 +POST,/questionnaire/companies/{id}/companies-repeating-block-2/,10,0,17,17.478320689406246,15.56993497069925,21.750068990513682,281.0,0.16827041003026288,0.0,17,18,18,19,22,22,22,22,22,22,22 +GET,/questionnaire/currency-total-playback-1/,10,0,26,25.991786795202643,24.673041072674096,27.197445975616574,105447.0,0.16827041003026288,0.0,26,26,27,27,27,27,27,27,27,27,27 +POST,/questionnaire/currency-total-playback-1/,10,0,15,15.363235981203616,14.145056018605828,18.488502013497055,271.0,0.16827041003026288,0.0,15,15,16,16,18,18,18,18,18,18,18 +GET,/questionnaire/distance-calculated-summary-1/,10,0,26,26.958028390072286,25.212451932020485,33.91596698202193,107231.0,0.16827041003026288,0.0,26,26,27,29,34,34,34,34,34,34,34 +POST,/questionnaire/distance-calculated-summary-1/,10,0,15,17.041597503703088,14.112673001363873,34.61689094547182,273.0,0.16827041003026288,0.0,15,15,15,17,35,35,35,35,35,35,35 +GET,/questionnaire/distance-calculated-summary-2/,10,0,26,27.42138138273731,25.336914928629994,31.599247944541276,107111.0,0.16827041003026288,0.0,26,28,29,30,32,32,32,32,32,32,32 +POST,/questionnaire/distance-calculated-summary-2/,10,0,16,16.347184986807406,14.384760987013578,21.528469980694354,273.0,0.16827041003026288,0.0,16,17,17,17,22,22,22,22,22,22,22 +GET,/questionnaire/distance-grand-calculated-summary/,10,0,26,27.186598512344062,25.21088405046612,35.17036803532392,106649.0,0.16827041003026288,0.0,26,27,27,28,35,35,35,35,35,35,35 +POST,/questionnaire/distance-grand-calculated-summary/,10,0,15,15.215421572793275,14.440665021538734,16.188485897146165,281.0,0.16827041003026288,0.0,15,15,16,16,16,16,16,16,16,16,16 +GET,/questionnaire/fourth-number-block/,10,0,28,28.731524804607034,26.62210096605122,32.096863025799394,104031.0,0.16827041003026288,0.0,28,29,30,31,32,32,32,32,32,32,32 +POST,/questionnaire/fourth-number-block/,10,0,17,17.6956680836156,16.26275002490729,21.105890977196395,277.0,0.16827041003026288,0.0,17,17,19,20,21,21,21,21,21,21,21 +GET,/questionnaire/grand-calculated-summary-first-number-block/,10,0,27,28.092170681338757,25.621325941756368,32.35362295527011,103212.0,0.16827041003026288,0.0,28,28,29,29,32,32,32,32,32,32,32 +POST,/questionnaire/grand-calculated-summary-first-number-block/,10,0,17,18.501907016616315,16.26997604034841,22.67963602207601,307.0,0.16827041003026288,0.0,18,19,20,21,23,23,23,23,23,23,23 +GET,/questionnaire/grand-calculated-summary-second-number-block/,10,0,27,27.77743588667363,26.732837897725403,28.838922968134284,104109.0,0.16827041003026288,0.0,28,28,29,29,29,29,29,29,29,29,29 +POST,/questionnaire/grand-calculated-summary-second-number-block/,10,0,17,17.26736529963091,16.129455994814634,19.050556933507323,277.0,0.16827041003026288,0.0,17,17,19,19,19,19,19,19,19,19,19 +GET,/questionnaire/introduction-block/,10,0,27,28.128321992699057,24.617624934762716,36.184969008900225,102921.0,0.16827041003026288,0.0,28,28,30,30,36,36,36,36,36,36,36 +POST,/questionnaire/introduction-block/,10,0,16,16.819810320157558,14.043923001736403,19.904726999811828,237.0,0.16827041003026288,0.0,17,18,18,20,20,20,20,20,20,20,20 +GET,/questionnaire/list-collector/,20,0,29,28.637125651584938,26.174005935899913,32.87482797168195,106126.0,0.33654082006052577,0.0,29,29,31,31,33,33,33,33,33,33,33 +POST,/questionnaire/list-collector/,20,0,16,17.133900639601052,15.025158994831145,21.707257023081183,252.0,0.33654082006052577,0.0,16,17,18,20,21,22,22,22,22,22,22 +GET,/questionnaire/number-calculated-summary-1/,10,0,25.06849798373878,26.762340066488832,25.06849798373878,36.39646898955107,107174.0,0.16827041003026288,0.0,26,26,26,27,36,36,36,36,36,36,36 +POST,/questionnaire/number-calculated-summary-1/,10,0,16,15.766054100822657,14.917496009729803,16.795122995972633,305.0,0.16827041003026288,0.0,16,16,16,17,17,17,17,17,17,17,17 +GET,/questionnaire/number-calculated-summary-2/,10,0,28,29.673091298900545,24.701099027879536,43.17428101785481,107086.0,0.16827041003026288,0.0,29,31,32,32,43,43,43,43,43,43,43 +POST,/questionnaire/number-calculated-summary-2/,10,0,17,17.022436810657382,15.20423498004675,21.666259039193392,285.0,0.16827041003026288,0.0,17,17,17,19,22,22,22,22,22,22,22 +GET,/questionnaire/number-grand-calculated-summary/,10,0,26,26.58855898771435,25.433333939872682,28.5050020320341,107421.0,0.16827041003026288,0.0,27,27,27,27,29,29,29,29,29,29,29 +POST,/questionnaire/number-grand-calculated-summary/,10,0,15,15.796137298457325,14.5332160172984,17.235978972166777,253.0,0.16827041003026288,0.0,16,16,17,17,17,17,17,17,17,17,17 +GET,/questionnaire/people/add-person/,10,0,29,28.47801949828863,26.244669104926288,31.142375897616148,103536.0,0.16827041003026288,0.0,29,29,30,30,31,31,31,31,31,31,31 +POST,/questionnaire/people/add-person/,10,0,16,17.800712818279862,15.090629109181464,24.46487091947347,247.0,0.16827041003026288,0.0,18,18,20,20,24,24,24,24,24,24,24 +GET,/questionnaire/people/{id}/add-or-edit-primary-person/,10,0,28,28.526184894144535,25.846129981800914,31.753687071613967,103388.0,0.16827041003026288,0.0,29,29,30,31,32,32,32,32,32,32,32 +POST,/questionnaire/people/{id}/add-or-edit-primary-person/,10,0,15,16.360942495521158,14.966896967962384,20.22734296042472,247.0,0.16827041003026288,0.0,16,16,17,18,20,20,20,20,20,20,20 +GET,/questionnaire/people/{id}/currency-total-playback-2/,20,0,26,27.140947157749906,24.882465018890798,35.9009790699929,107510.0,0.33654082006052577,0.0,26,27,28,28,33,36,36,36,36,36,36 +POST,/questionnaire/people/{id}/currency-total-playback-2/,20,0,15,15.932991093723103,14.336524996906519,27.647967101074755,301.0,0.33654082006052577,0.0,15,16,16,16,20,28,28,28,28,28,28 +GET,/questionnaire/people/{id}/grand-calculated-summary-third-number-block/,20,0,26,28.15191480331123,25.683920015580952,35.238586948253214,104082.0,0.33654082006052577,0.0,27,29,30,30,34,35,35,35,35,35,35 +POST,/questionnaire/people/{id}/grand-calculated-summary-third-number-block/,20,0,17,18.63090189290233,15.944191021844745,27.298807981424034,297.0,0.33654082006052577,0.0,17,18,20,20,25,27,27,27,27,27,27 +GET,/questionnaire/people/{id}/mutually-exclusive-checkbox/,20,0,29,30.849684780696407,27.999941958114505,46.14933708216995,106482.0,0.33654082006052577,0.0,29,30,32,32,38,46,46,46,46,46,46 +POST,/questionnaire/people/{id}/mutually-exclusive-checkbox/,20,0,17,18.320259114261717,16.936696018092334,25.426201056689024,281.0,0.33654082006052577,0.0,17,18,18,18,22,25,25,25,25,25,25 +GET,/questionnaire/people/{id}/set-min-max-block/,20,0,28,30.380786635214463,27.523183962330222,40.01657501794398,105566.0,0.33654082006052577,0.0,28,31,34,34,37,40,40,40,40,40,40 +POST,/questionnaire/people/{id}/set-min-max-block/,20,0,18,19.674770248821005,16.968537936918437,25.762334000319242,303.0,0.33654082006052577,0.0,18,21,22,23,24,26,26,26,26,26,26 +GET,/questionnaire/primary-person-list-collector/,10,0,28,28.62796471454203,26.188035029917955,32.09985001012683,102963.0,0.16827041003026288,0.0,28,29,30,32,32,32,32,32,32,32,32 +POST,/questionnaire/primary-person-list-collector/,10,0,16,17.051099834498018,15.541013097390532,19.65773105621338,299.0,0.16827041003026288,0.0,16,17,19,19,20,20,20,20,20,20,20 +GET,/questionnaire/responsible-party-business/,10,0,27,28.190073277801275,25.525647099129856,35.90523498132825,104384.0,0.16827041003026288,0.0,27,28,29,31,36,36,36,36,36,36,36 +POST,/questionnaire/responsible-party-business/,10,0,19,19.311857991851866,16.27724792342633,24.056772934272885,271.0,0.16827041003026288,0.0,20,20,20,21,24,24,24,24,24,24,24 +GET,/questionnaire/responsible-party/,10,0,28,28.231770684942603,26.809547911398113,30.833784956485033,103237.0,0.16827041003026288,0.0,28,29,29,30,31,31,31,31,31,31,31 +POST,/questionnaire/responsible-party/,10,0,17,18.198419269174337,15.82070195581764,21.607434027828276,269.0,0.16827041003026288,0.0,18,19,20,21,22,22,22,22,22,22,22 +GET,/questionnaire/second-number-block/,10,0,26,27.04410991864279,25.441315956413746,33.1697630463168,103676.0,0.16827041003026288,0.0,27,27,27,28,33,33,33,33,33,33,33 +POST,/questionnaire/second-number-block/,10,0,16,16.45449810894206,15.262449043802917,18.231995054520667,269.0,0.16827041003026288,0.0,16,17,17,17,18,18,18,18,18,18,18 +GET,/questionnaire/sections/calculated-summary-section/{id}/,20,0,28,28.50581799284555,26.30755095742643,32.931374036706984,111885.5,0.33654082006052577,0.0,28,29,30,31,32,33,33,33,33,33,33 +POST,/questionnaire/sections/calculated-summary-section/{id}/,20,0,12,12.543371896026656,10.795803042128682,17.2119700582698,319.0,0.33654082006052577,0.0,12,13,13,13,16,17,17,17,17,17,17 +GET,/questionnaire/sections/grand-calculated-summary-section-1/,10,0,27,26.878911699168384,24.99825495760888,29.059723019599915,109526.0,0.16827041003026288,0.0,27,28,28,29,29,29,29,29,29,29,29 +POST,/questionnaire/sections/grand-calculated-summary-section-1/,10,0,12,11.894893401768059,10.908885975368321,12.971151969395578,255.0,0.16827041003026288,0.0,12,12,12,13,13,13,13,13,13,13,13 +GET,/questionnaire/sections/questions-section/,10,0,25,26.00394559558481,24.42492393311113,30.237193102948368,106134.0,0.16827041003026288,0.0,26,26,27,28,30,30,30,30,30,30,30 +POST,/questionnaire/sections/questions-section/,10,0,11,11.733577097766101,10.506162070669234,14.970474992878735,333.0,0.16827041003026288,0.0,11,12,12,14,15,15,15,15,15,15,15 +GET,/questionnaire/sections/section-businesses/,10,0,26,30.75341918738559,24.09891993738711,71.59794098697603,106389.0,0.16827041003026288,0.0,26,26,29,30,72,72,72,72,72,72,72 +POST,/questionnaire/sections/section-businesses/,10,0,12,11.757753603160381,10.716030024923384,14.510384993627667,217.0,0.16827041003026288,0.0,12,12,12,12,15,15,15,15,15,15,15 +GET,/questionnaire/sections/section-companies/,10,0,32,34.451518091373146,27.63120597228408,62.01387196779251,118468.0,0.16827041003026288,0.0,32,34,34,36,62,62,62,62,62,62,62 +POST,/questionnaire/sections/section-companies/,10,0,12,12.594723678193986,10.99176099523902,16.99272694531828,217.0,0.16827041003026288,0.0,12,13,13,13,17,17,17,17,17,17,17 +GET,/questionnaire/skip-first-block/,10,0,27,27.238223515450954,25.755911017768085,29.09757010638714,103122.0,0.16827041003026288,0.0,27,27,28,29,29,29,29,29,29,29,29 +POST,/questionnaire/skip-first-block/,10,0,16,16.659896483179182,15.223064925521612,21.316227968782187,257.0,0.16827041003026288,0.0,16,17,17,17,21,21,21,21,21,21,21 +GET,/questionnaire/third-number-block/,10,0,27,28.251892584376037,25.959860999137163,32.420119969174266,103176.0,0.16827041003026288,0.0,28,28,29,30,32,32,32,32,32,32,32 +POST,/questionnaire/third-number-block/,10,0,17,18.482219707220793,16.706367023289204,20.714276004582644,257.0,0.16827041003026288,0.0,19,19,20,20,21,21,21,21,21,21,21 +GET,/session,10,0,160.0,159.4516820157878,154.2557330103591,179.00221806485206,217.0,0.16827041003026288,0.0,160,160,160,160,180,180,180,180,180,180,180 +GET,/submitted/download-pdf,10,0,2200.0,2994.80036071036,2120.7086900249124,4710.857865982689,20775.1,0.16827041003026288,0.0,2800,3700,3900,4100,4700,4700,4700,4700,4700,4700,4700 +GET,/submitted/feedback/send,10,0,28,29.466230585239828,27.52751694060862,34.44048692472279,106342.0,0.16827041003026288,0.0,28,29,31,33,34,34,34,34,34,34,34 +POST,/submitted/feedback/send,9,0,120.0,121.27008144226339,118.64531796891242,124.89975499920547,235.0,0.1514433690272366,0.0,120,120,120,120,120,120,120,120,120,120,120 +GET,/submitted/feedback/sent,9,0,24,24.43193111361729,23.565704934298992,26.932036969810724,102849.0,0.1514433690272366,0.0,24,24,24,27,27,27,27,27,27,27,27 +GET,/submitted/thank-you/,29,0,27,27.370365782127042,24.496339028701186,33.864647964946926,103505.0,0.48798418908776237,0.0,27,29,29,29,32,33,34,34,34,34,34 +GET,/submitted/view-response/,10,0,39,39.50409650569782,37.48978895600885,44.821103918366134,142934.0,0.16827041003026288,0.0,39,40,40,41,45,45,45,45,45,45,45 +,Aggregated,1097,0,25,53.09622206862289,10.506162070669234,4710.857865982689,54728.734731084776,18.45926398031984,0.0,25,27,28,29,32,38,130,180,4100,4700,4700 diff --git a/tests/mock_stats/2024-02-12T03:09:41/mock_output_stats.csv b/tests/mock_stats/2024-02-12T03:09:41/mock_output_stats.csv new file mode 100644 index 00000000..6aa2b23b --- /dev/null +++ b/tests/mock_stats/2024-02-12T03:09:41/mock_output_stats.csv @@ -0,0 +1,96 @@ +Type,Name,Request Count,Failure Count,Median Response Time,Average Response Time,Min Response Time,Max Response Time,Average Content Size,Requests/s,Failures/s,50%,66%,75%,80%,90%,95%,98%,99%,99.9%,99.99%,100% +GET,/questionnaire,11,0,11,12.023728647777302,10.659591993317008,16.86776103451848,255.0,0.18483579903286773,0.0,11,12,12,12,13,17,17,17,17,17,17 +GET,/questionnaire/,20,0,27,27.822096494492143,25.893958983942866,31.30856400821358,124766.5,0.33606508915066857,0.0,27,28,29,30,30,31,31,31,31,31,31 +POST,/questionnaire/,20,0,13,70.47625207924284,11.11529697664082,185.51862495951355,250.0,0.33606508915066857,0.0,120,120,120,120,130,190,190,190,190,190,190 +GET,/questionnaire/any-businesses-or-branches/,10,0,27,27.335992793086916,25.67505999468267,30.2064890274778,104244.0,0.16803254457533429,0.0,27,28,28,29,30,30,30,30,30,30,30 +POST,/questionnaire/any-businesses-or-branches/,10,0,17,17.933263699524105,16.102439956739545,21.13697910681367,273.0,0.16803254457533429,0.0,17,18,20,21,21,21,21,21,21,21,21 +GET,/questionnaire/any-companies-or-branches/,10,0,27,27.461823297198862,25.768079911358654,29.576422995887697,104222.0,0.16803254457533429,0.0,28,28,29,29,30,30,30,30,30,30,30 +POST,/questionnaire/any-companies-or-branches/,10,0,16,16.89466502284631,15.855325968004763,20.083628012798727,331.0,0.16803254457533429,0.0,17,17,17,18,20,20,20,20,20,20,20 +GET,/questionnaire/any-other-companies-or-branches/,10,0,29,33.64940480096266,27.694375021383166,75.47839696053416,106951.0,0.16803254457533429,0.0,29,29,30,33,75,75,75,75,75,75,75 +POST,/questionnaire/any-other-companies-or-branches/,10,0,17,17.985032498836517,16.167290974408388,21.366916014812887,269.0,0.16803254457533429,0.0,17,19,20,21,21,21,21,21,21,21,21 +GET,/questionnaire/any-other-trading-details/,10,0,27,27.282707183621824,25.096507975831628,30.944325029850006,103072.0,0.16803254457533429,0.0,27,28,28,29,31,31,31,31,31,31,31 +POST,/questionnaire/any-other-trading-details/,10,0,16,16.9910132070072,15.270124073140323,19.96381103526801,271.0,0.16803254457533429,0.0,17,17,18,19,20,20,20,20,20,20,20 +GET,/questionnaire/block-379/,11,0,28,29.262934398668055,26.443929062224925,40.021431050263345,103881.0,0.18483579903286773,0.0,28,29,30,30,30,40,40,40,40,40,40 +POST,/questionnaire/block-379/,11,0,16,16.81917956606908,15.54040506016463,19.282479071989655,237.0,0.18483579903286773,0.0,16,17,18,18,18,19,19,19,19,19,19 +GET,/questionnaire/block-381/,11,0,28,29.239173184826292,26.50415094103664,33.66381209343672,107705.0,0.18483579903286773,0.0,28,31,31,31,32,34,34,34,34,34,34 +POST,/questionnaire/block-381/,11,0,18,18.82509716828777,16.460436978377402,24.544372921809554,239.0,0.18483579903286773,0.0,18,19,20,20,21,25,25,25,25,25,25 +GET,/questionnaire/block-383/,11,0,27,27.14749553706497,24.845288950018585,32.74102695286274,103527.0,0.18483579903286773,0.0,27,28,28,28,29,33,33,33,33,33,33 +POST,/questionnaire/block-383/,11,0,16,16.259432188235223,14.399928972125053,19.27156699821353,277.0,0.18483579903286773,0.0,16,17,17,17,18,19,19,19,19,19,19 +GET,/questionnaire/block-4616/,11,0,26,28.267299618825994,25.36962495651096,39.74363300949335,103887.0,0.18483579903286773,0.0,26,29,30,30,30,40,40,40,40,40,40 +POST,/questionnaire/block-4616/,11,0,17,17.35131273215467,15.40341298095882,20.49127104692161,239.0,0.18483579903286773,0.0,17,18,19,19,20,20,20,20,20,20,20 +GET,/questionnaire/block-4952/,11,0,29,28.558125349015675,26.32978104520589,33.233374007977545,104489.0,0.18483579903286773,0.0,29,30,30,30,31,33,33,33,33,33,33 +POST,/questionnaire/block-4952/,11,0,16,16.94978934458711,15.46619099099189,20.28111508116126,239.0,0.18483579903286773,0.0,16,17,18,18,20,20,20,20,20,20,20 +GET,/questionnaire/block-4953/,11,0,28,30.14042670838535,26.912695029750466,43.50681998766959,106833.0,0.18483579903286773,0.0,28,29,31,31,35,44,44,44,44,44,44 +POST,/questionnaire/block-4953/,11,0,18,17.694243822585452,15.729359001852572,21.3465120177716,237.0,0.18483579903286773,0.0,18,19,19,19,19,21,21,21,21,21,21 +GET,/questionnaire/companies/add-company/?previous=any-companies-or-branches,10,0,27,28.427368181291968,25.55144694633782,36.88562905881554,103154.0,0.16803254457533429,0.0,28,29,30,30,37,37,37,37,37,37,37 +POST,/questionnaire/companies/add-company/?previous=any-companies-or-branches,10,0,17,16.946173075120896,15.525153954513371,21.091613918542862,307.0,0.16803254457533429,0.0,17,17,17,18,21,21,21,21,21,21,21 +GET,/questionnaire/companies/{id}/companies-repeating-block-1/,10,0,31,31.832728488370776,28.245499939657748,35.611258004792035,105200.0,0.16803254457533429,0.0,32,33,34,34,36,36,36,36,36,36,36 +POST,/questionnaire/companies/{id}/companies-repeating-block-1/,10,0,18,18.056010187137872,16.234264941886067,22.1514260629192,307.0,0.16803254457533429,0.0,18,18,18,19,22,22,22,22,22,22,22 +GET,/questionnaire/companies/{id}/companies-repeating-block-2/,10,0,28,28.515839506872,27.04095304943621,31.27285896334797,106197.0,0.16803254457533429,0.0,29,29,29,29,31,31,31,31,31,31,31 +POST,/questionnaire/companies/{id}/companies-repeating-block-2/,10,0,17,26.59225690877065,15.712260967120528,71.17931498214602,281.0,0.16803254457533429,0.0,17,17,17,60,71,71,71,71,71,71,71 +GET,/questionnaire/currency-total-playback-1/,11,0,25,25.79123854891143,24.590146029368043,27.532499050721526,105447.0,0.18483579903286773,0.0,25,27,27,27,27,28,28,28,28,28,28 +POST,/questionnaire/currency-total-playback-1/,11,0,15,15.217405172403563,14.311515958979726,16.584786935709417,271.0,0.18483579903286773,0.0,15,16,16,16,16,17,17,17,17,17,17 +GET,/questionnaire/distance-calculated-summary-1/,10,0,25,25.557694514282048,24.973108083941042,26.436373009346426,107231.0,0.16803254457533429,0.0,26,26,26,26,26,26,26,26,26,26,26 +POST,/questionnaire/distance-calculated-summary-1/,10,0,15,14.863740419968963,14.354609069414437,15.64662007149309,273.0,0.16803254457533429,0.0,15,15,15,16,16,16,16,16,16,16,16 +GET,/questionnaire/distance-calculated-summary-2/,10,0,26,26.89779318170622,25.148609071038663,29.60843697655946,107111.0,0.16803254457533429,0.0,27,27,28,29,30,30,30,30,30,30,30 +POST,/questionnaire/distance-calculated-summary-2/,10,0,15,15.710299997590482,14.467647997662425,18.956255982629955,273.0,0.16803254457533429,0.0,15,15,17,17,19,19,19,19,19,19,19 +GET,/questionnaire/distance-grand-calculated-summary/,10,0,27,27.285301266238093,24.897646042518318,31.329549965448678,106649.0,0.16803254457533429,0.0,27,28,29,29,31,31,31,31,31,31,31 +POST,/questionnaire/distance-grand-calculated-summary/,10,0,15,15.699037618469447,14.237706083804369,18.080833018757403,281.0,0.16803254457533429,0.0,16,16,17,17,18,18,18,18,18,18,18 +GET,/questionnaire/fourth-number-block/,10,0,28,28.12962430762127,26.47744701243937,30.249361065216362,104031.0,0.16803254457533429,0.0,28,29,29,30,30,30,30,30,30,30,30 +POST,/questionnaire/fourth-number-block/,10,0,17,17.136811593081802,15.666393912397325,19.444087985903025,277.0,0.16803254457533429,0.0,17,17,18,18,19,19,19,19,19,19,19 +GET,/questionnaire/grand-calculated-summary-first-number-block/,10,0,26,26.822160417214036,25.580993969924748,29.98897701036185,103212.0,0.16803254457533429,0.0,26,26,28,29,30,30,30,30,30,30,30 +POST,/questionnaire/grand-calculated-summary-first-number-block/,10,0,16,16.614362399559468,15.781100024469197,18.95773597061634,307.0,0.16803254457533429,0.0,16,17,17,17,19,19,19,19,19,19,19 +GET,/questionnaire/grand-calculated-summary-second-number-block/,10,0,27,27.427900524344295,26.261829072609544,30.83911002613604,104109.0,0.16803254457533429,0.0,27,27,28,29,31,31,31,31,31,31,31 +POST,/questionnaire/grand-calculated-summary-second-number-block/,10,0,16,17.074522899929434,15.845473972149193,20.658474997617304,277.0,0.16803254457533429,0.0,17,17,17,18,21,21,21,21,21,21,21 +GET,/questionnaire/introduction-block/,11,0,26,27.6235130996528,24.181190063245595,44.168516993522644,102921.0,0.18483579903286773,0.0,26,27,28,28,28,44,44,44,44,44,44 +POST,/questionnaire/introduction-block/,11,0,15,15.390935459766876,13.959693955257535,17.118699033744633,237.0,0.18483579903286773,0.0,15,15,17,17,17,17,17,17,17,17,17 +GET,/questionnaire/list-collector/,22,0,28,29.472088213713672,25.900017004460096,54.458655999042094,106126.0,0.36967159806573546,0.0,28,29,30,30,31,33,54,54,54,54,54 +POST,/questionnaire/list-collector/,22,0,17,17.382982737300072,14.940560911782086,22.597166011109948,252.0,0.36967159806573546,0.0,17,18,19,19,19,19,23,23,23,23,23 +GET,/questionnaire/number-calculated-summary-1/,10,0,25,25.545710476581007,24.545509018935263,27.720846934244037,107174.0,0.16803254457533429,0.0,25,26,26,26,28,28,28,28,28,28,28 +POST,/questionnaire/number-calculated-summary-1/,10,0,15,16.258963895961642,14.39303602091968,20.91054094489664,305.0,0.16803254457533429,0.0,15,16,19,19,21,21,21,21,21,21,21 +GET,/questionnaire/number-calculated-summary-2/,10,0,26,26.225437386892736,24.55532201565802,30.877685989253223,107086.0,0.16803254457533429,0.0,26,26,26,27,31,31,31,31,31,31,31 +POST,/questionnaire/number-calculated-summary-2/,10,0,16,16.186045680660754,14.696788974106312,18.164873938076198,285.0,0.16803254457533429,0.0,16,16,17,17,18,18,18,18,18,18,18 +GET,/questionnaire/number-grand-calculated-summary/,10,0,27,27.126433479133993,25.356811936944723,31.230952008627355,107421.0,0.16803254457533429,0.0,27,27,27,29,31,31,31,31,31,31,31 +POST,/questionnaire/number-grand-calculated-summary/,10,0,15,15.870061598252505,14.255363959819078,18.072025035507977,253.0,0.16803254457533429,0.0,16,16,17,18,18,18,18,18,18,18,18 +GET,/questionnaire/people/add-person/,11,0,27,28.25361707205461,25.949920993298292,33.061775960959494,103536.0,0.18483579903286773,0.0,27,29,31,31,31,33,33,33,33,33,33 +POST,/questionnaire/people/add-person/,11,0,16,15.967307083139366,14.423005981370807,18.14297295641154,247.0,0.18483579903286773,0.0,16,16,17,17,18,18,18,18,18,18,18 +GET,/questionnaire/people/{id}/add-or-edit-primary-person/,11,0,27,27.788131434301082,25.6284800125286,31.455648015253246,103388.0,0.18483579903286773,0.0,27,29,30,30,31,31,31,31,31,31,31 +POST,/questionnaire/people/{id}/add-or-edit-primary-person/,11,0,16,16.341146367432717,14.625110081396997,19.73554096184671,247.0,0.18483579903286773,0.0,16,17,17,17,18,20,20,20,20,20,20 +GET,/questionnaire/people/{id}/currency-total-playback-2/,22,0,26,26.033330221914433,24.840443045832217,29.136425931937993,107510.0,0.36967159806573546,0.0,26,26,26,27,27,28,29,29,29,29,29 +POST,/questionnaire/people/{id}/currency-total-playback-2/,22,0,15,14.866983824917538,14.102965011261404,16.571006970480084,301.0,0.36967159806573546,0.0,15,15,15,15,16,16,17,17,17,17,17 +GET,/questionnaire/people/{id}/grand-calculated-summary-third-number-block/,22,0,27,27.216435986867342,25.53047996480018,31.706031993962824,104082.0,0.36967159806573546,0.0,27,27,28,28,29,31,32,32,32,32,32 +POST,/questionnaire/people/{id}/grand-calculated-summary-third-number-block/,22,0,17,17.354623451617293,16.052633989602327,19.64696403592825,297.0,0.36967159806573546,0.0,17,18,18,18,19,20,20,20,20,20,20 +GET,/questionnaire/people/{id}/mutually-exclusive-checkbox/,22,0,29,29.089198961049657,28.18134892731905,32.69398398697376,106482.0,0.36967159806573546,0.0,29,29,30,30,30,30,33,33,33,33,33 +POST,/questionnaire/people/{id}/mutually-exclusive-checkbox/,22,0,17,17.72611483995041,16.758193029090762,19.687114981934428,281.0,0.36967159806573546,0.0,18,18,18,19,19,19,20,20,20,20,20 +GET,/questionnaire/people/{id}/set-min-max-block/,22,0,28,28.6779242715883,27.23730495199561,32.71366294939071,105566.0,0.36967159806573546,0.0,28,29,30,30,31,31,33,33,33,33,33 +POST,/questionnaire/people/{id}/set-min-max-block/,22,0,18,18.47250515658578,16.97343692649156,26.118434965610504,303.0,0.36967159806573546,0.0,18,18,18,19,20,21,26,26,26,26,26 +GET,/questionnaire/primary-person-list-collector/,11,0,28,27.347312446429648,25.326667935587466,30.411778017878532,102963.0,0.18483579903286773,0.0,28,28,28,28,29,30,30,30,30,30,30 +POST,/questionnaire/primary-person-list-collector/,11,0,16,16.617663627998397,14.992184937000275,19.398897071368992,299.0,0.18483579903286773,0.0,16,17,18,18,18,19,19,19,19,19,19 +GET,/questionnaire/responsible-party-business/,10,0,27,27.359139104373753,25.207280996255577,31.138586928136647,104384.0,0.16803254457533429,0.0,27,28,29,29,31,31,31,31,31,31,31 +POST,/questionnaire/responsible-party-business/,10,0,18,20.860162819735706,15.71378402877599,43.15320204477757,271.0,0.16803254457533429,0.0,19,19,21,22,43,43,43,43,43,43,43 +GET,/questionnaire/responsible-party/,10,0,27,27.0687646814622,25.491180014796555,30.16620094422251,103237.0,0.16803254457533429,0.0,27,28,28,28,30,30,30,30,30,30,30 +POST,/questionnaire/responsible-party/,10,0,16,16.51082979515195,15.771970967762172,17.900313017889857,269.0,0.16803254457533429,0.0,16,16,17,18,18,18,18,18,18,18,18 +GET,/questionnaire/second-number-block/,11,0,26,26.420731997032735,25.023204972967505,28.467142023146152,103676.0,0.18483579903286773,0.0,26,27,27,27,28,28,28,28,28,28,28 +POST,/questionnaire/second-number-block/,11,0,16,16.451824636367913,15.505062998272479,19.18758498504758,269.0,0.18483579903286773,0.0,16,17,17,17,18,19,19,19,19,19,19 +GET,/questionnaire/sections/calculated-summary-section/{id}/,22,0,27,27.969528079583224,26.076174923218787,41.80481797084212,111885.5,0.36967159806573546,0.0,27,27,27,28,30,32,42,42,42,42,42 +POST,/questionnaire/sections/calculated-summary-section/{id}/,22,0,11,11.792160538871856,10.615107952617109,14.324543997645378,319.0,0.36967159806573546,0.0,11,12,12,14,14,14,14,14,14,14,14 +GET,/questionnaire/sections/grand-calculated-summary-section-1/,10,0,26,30.542609677650034,24.657204980030656,62.56481597665697,109526.0,0.16803254457533429,0.0,27,28,30,32,63,63,63,63,63,63,63 +POST,/questionnaire/sections/grand-calculated-summary-section-1/,10,0,11,11.934103991370648,10.609688004478812,15.364074963144958,255.0,0.16803254457533429,0.0,12,12,13,13,15,15,15,15,15,15,15 +GET,/questionnaire/sections/questions-section/,11,0,25,25.554111174477097,24.403361952863634,29.54151900485158,106134.0,0.18483579903286773,0.0,25,26,26,26,27,30,30,30,30,30,30 +POST,/questionnaire/sections/questions-section/,11,0,12,11.84888310010799,10.717255063354969,13.678024988621473,333.0,0.18483579903286773,0.0,12,12,13,13,13,14,14,14,14,14,14 +GET,/questionnaire/sections/section-businesses/,10,0,26,25.659533601719886,23.98305502720177,28.549611917696893,106389.0,0.16803254457533429,0.0,26,26,26,27,29,29,29,29,29,29,29 +POST,/questionnaire/sections/section-businesses/,10,0,11,11.572326358873397,10.67960704676807,12.709217960946262,217.0,0.16803254457533429,0.0,12,12,12,12,13,13,13,13,13,13,13 +GET,/questionnaire/sections/section-companies/,10,0,28,28.882129304111004,27.452037087641656,32.60107210371643,118468.0,0.16803254457533429,0.0,29,30,30,30,33,33,33,33,33,33,33 +POST,/questionnaire/sections/section-companies/,10,0,12,11.53269688365981,10.884089046157897,12.649790034629405,217.0,0.16803254457533429,0.0,12,12,12,12,13,13,13,13,13,13,13 +GET,/questionnaire/skip-first-block/,11,0,27,27.013281201520428,25.26564395520836,29.000431997701526,103122.0,0.18483579903286773,0.0,27,28,28,28,28,29,29,29,29,29,29 +POST,/questionnaire/skip-first-block/,11,0,16,16.28173692998561,15.399846946820617,17.30755204334855,257.0,0.18483579903286773,0.0,16,17,17,17,17,17,17,17,17,17,17 +GET,/questionnaire/third-number-block/,10,0,28,35.814188595395535,25.51633003167808,110.29883194714785,103176.0,0.16803254457533429,0.0,28,29,29,29,110,110,110,110,110,110,110 +POST,/questionnaire/third-number-block/,10,0,17,19.61497610900551,15.962035045959055,40.94952798914164,257.0,0.16803254457533429,0.0,17,18,18,20,41,41,41,41,41,41,41 +GET,/session,11,0,160.0,190.8947893693535,152.82216493505985,441.3383030332625,217.0,0.18483579903286773,0.0,160,160,170,170,240,440,440,440,440,440,440 +GET,/submitted/download-pdf,10,0,2100.0,2846.635233727284,2059.2743470333517,4959.792048088275,20850.3,0.16803254457533429,0.0,2200,2300,3900,4800,5000,5000,5000,5000,5000,5000,5000 +GET,/submitted/feedback/send,10,0,28,28.79959971178323,27.299063047394156,31.47279191762209,106342.0,0.16803254457533429,0.0,29,29,30,31,31,31,31,31,31,31,31 +POST,/submitted/feedback/send,10,0,120.0,121.63299061357975,119.44213102106005,123.58560203574598,235.0,0.16803254457533429,0.0,120,120,120,120,120,120,120,120,120,120,120 +GET,/submitted/feedback/sent,10,0,25,29.2888872907497,24.152715923264623,65.0689989561215,102849.0,0.16803254457533429,0.0,26,26,26,27,65,65,65,65,65,65,65 +GET,/submitted/thank-you/,30,0,27,27.305805997457355,24.546172004193068,33.88853895012289,103505.0,0.5040976337260029,0.0,28,28,29,29,30,31,34,34,34,34,34 +GET,/submitted/view-response/,10,0,39,39.77046888321638,37.52195311244577,42.70393692422658,142934.0,0.16803254457533429,0.0,40,40,41,42,43,43,43,43,43,43,43 +,Aggregated,1154,0,25,50.44127832155956,10.609688004478812,4959.792048088275,54590.278162911614,19.39095564399358,0.0,25,27,28,28,30,40,120,240,4800,5000,5000 diff --git a/tests/test_benchmark_stats.py b/tests/test_benchmark_stats.py index 43fc67c7..cfcd3023 100644 --- a/tests/test_benchmark_stats.py +++ b/tests/test_benchmark_stats.py @@ -3,7 +3,9 @@ from scripts.benchmark_stats import BenchmarkStats from tests.conftest import ( EXPECTED_OUTPUT_MULTIPLE_FOLDERS, + EXPECTED_OUTPUT_MULTIPLE_FOLDERS_WITH_PDF, EXPECTED_OUTPUT_SINGLE_FOLDER, + EXPECTED_OUTPUT_SINGLE_FOLDER_WITH_PDF, ) @@ -12,6 +14,11 @@ def benchmark_stats(): return BenchmarkStats(folder_paths=["./tests/mock_stats/2024-02-07T03:09:41"]) +@pytest.fixture +def benchmark_stats_pdf(): + return BenchmarkStats(folder_paths=["./tests/mock_stats/2024-02-09T03:09:41"]) + + @pytest.fixture def benchmark_stats_multiple(): return BenchmarkStats( @@ -22,12 +29,32 @@ def benchmark_stats_multiple(): ) -def test_formatted_percentiles(benchmark_stats): - assert EXPECTED_OUTPUT_SINGLE_FOLDER == str(benchmark_stats) +@pytest.fixture +def benchmark_stats_multiple_with_pdf(): + return BenchmarkStats( + folder_paths=[ + "./tests/mock_stats/2024-02-07T03:09:41", + "./tests/mock_stats/2024-02-06T03:09:41", + "./tests/mock_stats/2024-02-09T03:09:41", + "./tests/mock_stats/2024-02-12T03:09:41", + ] + ) -def test_formatted_percentiles_multiple_folders(benchmark_stats_multiple): - assert EXPECTED_OUTPUT_MULTIPLE_FOLDERS == str(benchmark_stats_multiple) +@pytest.mark.parametrize( + "benchmark_stats_fixture, expected_result", + ( + ("benchmark_stats", EXPECTED_OUTPUT_SINGLE_FOLDER), + ("benchmark_stats_pdf", EXPECTED_OUTPUT_SINGLE_FOLDER_WITH_PDF), + ("benchmark_stats_multiple", EXPECTED_OUTPUT_MULTIPLE_FOLDERS), + ( + "benchmark_stats_multiple_with_pdf", + EXPECTED_OUTPUT_MULTIPLE_FOLDERS_WITH_PDF, + ), + ), +) +def test_formatted_percentile(benchmark_stats_fixture, expected_result, request): + assert str(request.getfixturevalue(benchmark_stats_fixture)) == expected_result def test_files(benchmark_stats): @@ -37,7 +64,7 @@ def test_files(benchmark_stats): def test_percentiles(benchmark_stats): - assert benchmark_stats.percentiles == {50: 58, 90: 96, 95: 173, 99: 301, 99.9: 477} + assert benchmark_stats.percentiles == {50: 58, 90: 97, 95: 174, 99: 301, 99.9: 477} def test_total_requests(benchmark_stats): @@ -49,8 +76,45 @@ def test_average_get(benchmark_stats): def test_average_post(benchmark_stats): - assert benchmark_stats.average_post == 211 + assert benchmark_stats.average_post == 212 def test_error_percentage(benchmark_stats): assert benchmark_stats.error_percentage == 0.0014156285390713476 + + +@pytest.mark.parametrize( + "benchmark_stats_fixture, percentile_value", + ( + ("benchmark_stats_pdf", 4700), + ("benchmark_stats_multiple_with_pdf", 4850), + ), +) +def test_pdf_percentile(benchmark_stats_fixture, percentile_value, request): + assert ( + request.getfixturevalue(benchmark_stats_fixture) + ).average_pdf_percentile == percentile_value + + +def test_non_applicable_pdf_percentile(benchmark_stats): + assert benchmark_stats.average_pdf_percentile is None + + +def test_session_percentile(benchmark_stats_pdf): + assert benchmark_stats_pdf.average_session_percentile == 180 + + +def test_no_pdf_endpoint_formatted_percentile(benchmark_stats): + assert ( + benchmark_stats.formatted_percentile(benchmark_stats.average_pdf_percentile) + == "N/A" + ) + + +def test_formatted_pdf_percentile(benchmark_stats_pdf): + assert ( + benchmark_stats_pdf.formatted_percentile( + benchmark_stats_pdf.average_pdf_percentile + ) + == "4700ms" + ) diff --git a/tests/test_get_summary.py b/tests/test_get_summary.py index fdc4a38c..5e69e018 100644 --- a/tests/test_get_summary.py +++ b/tests/test_get_summary.py @@ -8,13 +8,16 @@ "---\n" "Percentile Averages:\n" "50th: 58ms\n" - "90th: 99ms\n" - "95th: 177ms\n" - "99th: 319ms\n" + "90th: 100ms\n" + "95th: 178ms\n" + "99th: 320ms\n" "99.9th: 654ms\n" "---\n" - "GETs (99th): 384ms\n" - "POSTs (99th): 245ms\n" + "GETs (99th): 385ms\n" + "POSTs (99th): 246ms\n" + "---\n" + "PDF: N/A\n" + "Session: 7150ms\n" "---\n" "Total Requests: 141,201\n" "Total Failures: 1\n" @@ -26,12 +29,14 @@ "**Benchmark Results**

" "Percentile Averages:
" "50th: 58ms
" - "90th: 96ms
" - "95th: 173ms
" + "90th: 97ms
" + "95th: 174ms
" "99th: 301ms
" "99.9th: 477ms
" "GETs (99th): 380ms
" - "POSTs (99th): 211ms

" + "POSTs (99th): 212ms

" + "PDF: N/A
" + "Session: 7600ms

" "Total Requests: 70,640
" "Total Failures: 1
" "Error Percentage: 0.0%
" diff --git a/tests/test_visualise_results.py b/tests/test_visualise_results.py index 503f3b51..665a55c0 100644 --- a/tests/test_visualise_results.py +++ b/tests/test_visualise_results.py @@ -1,62 +1,194 @@ +from glob import glob + import pytest +from freezegun import freeze_time from pandas import DataFrame from pandas.testing import assert_frame_equal from scripts.get_summary import get_results -from scripts.visualise_results import GraphGenerationFailed, get_data_frame, plot_data +from scripts.visualise_results import ( + GraphGenerationFailed, + create_graph, + get_additional_metrics_data_frame, + get_dataframes, + get_performance_data_frame, +) -expected_data_frame = DataFrame.from_dict( - {"DATE": ["2024-02-07"], "50th": [58], "90th": [96], "95th": [173], "99th": [301]} +expected_performance_dataframe = DataFrame.from_dict( + {"DATE": ["2024-02-07"], "50th": [58], "90th": [97], "95th": [174], "99th": [301]} ) -expected_data_frame_multiple_files = DataFrame.from_dict( +expected_pdf_session_dataframe = DataFrame.from_dict( + {"DATE": ["2024-02-09"], "PDF": [4700], "Session": [180]} +) + +expected_performance_dataframe_consecutive_days = DataFrame.from_dict( { "DATE": ["2024-02-07", "2024-02-06"], "50th": [58, 58], - "90th": [96, 99], - "95th": [173, 177], - "99th": [301, 319], + "90th": [97, 100], + "95th": [174, 178], + "99th": [301, 320], } ) +expected_pdf_session_dataframe_consecutive_days = DataFrame.from_dict( + { + "DATE": ["2024-02-09", "2024-02-12"], + "PDF": [4700, 5000], + "Session": [180, 440], + } +) -def test_get_data_frame_single_file(get_results_single_file): - dataframe = get_data_frame(get_results_single_file) - assert_frame_equal(dataframe, expected_data_frame) +expected_performance_dataframe_multiple_days = DataFrame.from_dict( + { + "DATE": ["2024-02-06", "2024-02-07", "2024-02-09", "2024-02-12"], + "50th": [58, 58, 52, 45], + "90th": [100, 97, 75, 75], + "95th": [178, 174, 76, 79], + "99th": [320, 301, 76, 80], + } +) + +expected_pdf_session_dataframe_multiple_days = DataFrame.from_dict( + { + "DATE": ["2024-02-06", "2024-02-07", "2024-02-09", "2024-02-12"], + "PDF": [None, None, 4700, 5000], + "Session": [7150, 7600, 180, 440], + } +) + +dataframes_list = [ + expected_performance_dataframe_multiple_days, + expected_pdf_session_dataframe_multiple_days, +] -def test_get_data_frame_multiple_files(): - results = get_results( - folders=[ - "./tests/mock_stats/2024-02-07T03:09:41", - "./tests/mock_stats/2024-02-06T03:09:41", - ] - ) - dataframe = get_data_frame(results) - assert_frame_equal(dataframe, expected_data_frame_multiple_files) +@pytest.mark.parametrize( + "results_file_fixture, data_frame_method, expected_result", + ( + ( + "get_results_single_file", + get_performance_data_frame, + expected_performance_dataframe, + ), + ( + "get_results_single_file_with_pdf_endpoint", + get_additional_metrics_data_frame, + expected_pdf_session_dataframe, + ), + ), +) +def test_get_data_frame_single_file( + results_file_fixture, data_frame_method, expected_result, request +): + dataframe = data_frame_method(request.getfixturevalue(results_file_fixture)) + assert_frame_equal(dataframe, expected_result) -def test_plot_data_df(mocker, get_results_single_file): - dataframe = get_data_frame(get_results_single_file) +@pytest.mark.parametrize( + "folders, data_frame_method, expected_result", + ( + ( + [ + "./tests/mock_stats/2024-02-07T03:09:41", + "./tests/mock_stats/2024-02-06T03:09:41", + ], + get_performance_data_frame, + expected_performance_dataframe_consecutive_days, + ), + ( + [ + "./tests/mock_stats/2024-02-09T03:09:41", + "./tests/mock_stats/2024-02-12T03:09:41", + ], + get_additional_metrics_data_frame, + expected_pdf_session_dataframe_consecutive_days, + ), + ), +) +def test_get_data_frame_multiple_files(folders, data_frame_method, expected_result): + results = get_results(folders=folders) + dataframe = data_frame_method(results) + assert_frame_equal(dataframe, expected_result) + + +@pytest.mark.parametrize( + "expected_dataframe, data_frame_method, results_file", + ( + ( + expected_performance_dataframe, + get_performance_data_frame, + "get_results_single_file", + ), + ( + expected_pdf_session_dataframe, + get_additional_metrics_data_frame, + "get_results_single_file_with_pdf_endpoint", + ), + ), +) +def test_plot_data_df( + expected_dataframe, data_frame_method, results_file, mocker, request +): + dataframe = data_frame_method(request.getfixturevalue(results_file)) mock_plot_data = mocker.patch("scripts.visualise_results.plot_data") mock_plot_data(dataframe, 1) assert mock_plot_data.call_count == 1 - assert_frame_equal(mock_plot_data.call_args[0][0], expected_data_frame) + assert_frame_equal(mock_plot_data.call_args[0][0], expected_dataframe) -def test_plot_data(mocker, get_results_single_file): - dataframe = get_data_frame(get_results_single_file) +@pytest.mark.parametrize( + "results_file, data_frame_method, image_name", + ( + ( + "get_results_single_file", + get_performance_data_frame, + "performance_graph.png", + ), + ( + "get_results_single_file_with_pdf_endpoint", + get_additional_metrics_data_frame, + "additional_metrics.png", + ), + ), +) +def test_individual_graph_creation( + results_file, data_frame_method, image_name, mocker, request +): + dataframe = data_frame_method(request.getfixturevalue(results_file)) try: - graph_output = mocker.patch("matplotlib.pyplot.savefig") - plot_data(dataframe, 1) - assert graph_output.call_count == 1 + graph_creation = mocker.patch("matplotlib.pyplot.subplot") + create_graph([dataframe], 1, image_name) + assert graph_creation.call_count == 1 except GraphGenerationFailed: pytest.fail("Graph generation failed") -def test_plot_data_failed(): +@freeze_time("2024-03-1") +def test_get_dataframes(): + folders = sorted(glob("tests/mock_stats/*")) + dataframes = get_dataframes(folders, 30) + + for i, item in enumerate(dataframes): + assert_frame_equal(item, dataframes_list[i]) + + +def test_create_graph_failed(): dataframe = DataFrame.from_dict({}) with pytest.raises(GraphGenerationFailed): - plot_data(dataframe, 1) + create_graph([dataframe], 1, "test_graph.png") + + +@freeze_time("2024-03-1") +def test_create_graph(mocker): + folders = sorted(glob("tests/mock_stats/*")) + dataframes = get_dataframes(folders, 30) + try: + graph_output = mocker.patch("matplotlib.pyplot.savefig") + create_graph(dataframes, 1, "performance_graph.png") + assert graph_output.call_count == 1 + except GraphGenerationFailed: + pytest.fail("Graph generation failed")