Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Evaluate meta tests 53 #8

Open
wants to merge 126 commits into
base: master
Choose a base branch
from

Conversation

andypants888
Copy link
Contributor

53 === jira task number

Basic Functionality of the "metaTests "is working now.

  • The MetaTests
    • Are "testing" the code-challenge tests "aka internalTests".
    • Are basically pass/fail examples of userCode
    • Should see if internalTests are passing/failing correctly

Video Demo

The Pass/Fail detection currently works for only SOME challenges.

The code & output are still messy to read.

Actually I wasn't going to submit a PR yet,
but I wanted to get some feedback on variable names and conventions
and show what my current draft looks like.

PS:
most files are copied/modded from frontEnd test-evaluation files.
pMap had to be downgraded to version 4 for compatibility with require/import on nodejs

@@ -82,6 +82,7 @@ ssl
nbproject
public/uploads/*
!public/uploads/.gitkeep
.vscode/
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.vscode/launch.json is a config file that gets added to the project from the vscode debugger.

I wanted to keep all vscode config files out of our final repo

############################

src/api/code-challenge/content-types/code-challenge/*.js
!src/api/code-challenge/content-types/code-challenge/lifecycles.js
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these being ignored?

Copy link
Contributor Author

@andypants888 andypants888 Jun 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is my "scalable" way of dealing with the .js files created during typescript compilation.

lifecycles.js had an issue with typescript on strapi. Strapi V4 announced May 12, 2022 they would begin beta support for typescript in this post.

Do you want me to investigate the beta support? Currently we are not using TS for the strapi lifecycles hooks file.

We originally discussed it here:
#8 (comment)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would love it if you did that.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I don't think the compiled files should go in the same folder as the uncompiled files. I think they should go in adist folder or something. That would automatically solve the gitignore issue as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would love it if you did that.

To test the beta version of TypeScript support, run the command npx create-strapi-app@beta my-app (--ts) in a terminal.
I will need to rebuild the app. I will save this for the last part.

Can I restore the previous version using git reset --hard ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've decided to wait a month for strapi to continue working out bugs with their beta typescript support

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I don't think the compiled files should go in the same folder as the uncompiled files. I think they should go in adist folder or something. That would automatically solve the gitignore issue as well

I agree with the dist folder, but should the dist folder be a clone of the src folder or just a smaller folder within src? Currently very few files are being used with TS, and TS support is still in beta for strapi

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say a clone of the src folder but let me know if you have issues

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will wait until strapi integrates TS to implement the outDir & srcDir, since I'm not sure if running "strapi start" / "strapi develop" will work with a dist folder yet.

Comment on lines 7 to 8
// Currently unused function. Did you write this Oliver?
const getInternalLabel = async (event) => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I did. Can you just remove it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. It's gone now.

Comment on lines 108 to 129
async function runInternalTests(
internalTests,
metaCaseCode,
challengeLabel,
metaTestLabel,
metaTestId,
evalResultShouldBe,
count
) {
let results = await executeTests(
metaCaseCode,
internalTests,
metaTestLabel,
challengeLabel,
metaTestId,
evalResultShouldBe
);
// MetaTest Final Results being logged to Console.
console.log(`<METATEST EXAMPLE SET #${count + 1}>`, results);

return results;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like all this function does it just run executeTests, why not remove this function and call that directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I got rid of runInternalTests(), and now called executeTests directly

return internalTests;

// Rename internalTest to internalTestCode
internalTests.map((internalTestPackage) => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you need to have internalTestCode instead of internalTest? It seems unnecessarily confusing

Copy link
Contributor Author

@andypants888 andypants888 Jun 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

during the error output:

Screen Shot 2022-06-23 at 2 57 03 PM

and source code:
Screen Shot 2022-06-23 at 2 57 52 PM
Screen Shot 2022-06-23 at 2 58 14 PM

internalTest causes some confusion between internalTestCode vs internalTestLabel vs internalTestId inside the code

Originally, internalTest === internalTestCode

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a comment explaining why this renaming was needed in the code.

We could later rename "internalTest" --> "internalTestCode" in our database to clean up this code.

Comment on lines 61 to 67
if (evaluateWithContext(codeWithCondition, context)) {
return {
index: i,
passed: true,
};
}
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I honeslty think it'd be a bit more intuitive to read if you put evaluateWithContext(codeWithCondition, context) in a variable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is yours, but I tried to apply your suggestion.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jesus fuck

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I'm not setting the best example sorry.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although as far as examples go, my rushed code is still decent. Just not as good as my normal stuff

@@ -0,0 +1,101 @@
{
"compilerOptions": {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove the commented out code

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -0,0 +1,50 @@
// andy - Not Directly Used in MetaTests
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused. Why is this file added if it isn't used in metaTests? Is it just apart of the general migration to TS?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file is imported into:

lifecycles.js - const { removeEmpty } = require("../code-challenge/general");

utils.ts import { getErrorMessage } from "../code-challenge/general";

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove the comment then. Also, can you remove any unused functions and commented code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

};

const setNullError = () => {
evalError = {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like this thing you're doing here and in the above function. Clean javascript is usually according to a functional paradim, meaning that you generally pass in something and get something out. Here you are mutating something, which leads to things getting confusing. Just return a new object, and use that value to reassign the original variable. That might seem the same but it's cleaner to reassign at the outer level rather than within the function... it's more relevant. Let me know if you don't follow

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I tried to clean it up. Let me know if you want more changes.

Comment on lines 113 to 123
} finally {
determineReturnObject();
}
restoreConsoleLog();
return {
error: evalError,
userPassed: userPassed,
description: descriptionMessage,
resultType: typeof result,
evalResult: result,
};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This finally statement doesn't seem necessary. Rather than reassigning why not just fold it into the final return statement?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand. What do you mean by reassigning?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

determineReturnObject reassigns values and then you finally returns the reassigned variables (this by the way is an example of how the non-functional paradigm is making things confusing. Because when you see determineReturnObject it's not intuitive that that causes variable reassignments

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I tried to combine the var assignment with the return statement.

The vars need to be assigned after the try...catch block

Copy link
Owner

@oriooctopus oriooctopus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getting closer!

@@ -0,0 +1,30 @@
// @ts-expect-error will fix later
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove whichever functions are unused?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -0,0 +1,50 @@
// andy - Not Directly Used in MetaTests
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove the comment then. Also, can you remove any unused functions and commented code?


// runMetaTestsNeed more specific name later, maybe use "validateInternalTests"
async function runMetaTests(eventTests, eventMetaTests) {
const conductMetaTestsOnTests = async (
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I think runMetaTests would be a much better name. Why did you get rid of it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed back to runMetaTests. I didn't think it was descriptive enough. I am getting the feeling the naming conventions are somewhat subjective.

Is there some standard I can read on naming conventions? They exist in other fields like chemistry etc. Nomenclature?

async (internalTest) => {
const {
label: internalTestLabel,
internalTestCode: internalTestCode,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can just do internalTestCode, you don't need internalTestCode: internalTestCode

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I changed it.

Comment on lines 65 to 83
return {
short: {
description: null,
...pick(testEvaluatorResults, ["description"]),
},
long: {
challengeLabel,
metaTestId,
internalTestId,
internalTestLabel,
userPassed: null,
evalResultType: null,
...pick(testEvaluatorResults, [
"evalResultType",
"userPassed",
"evalError",
]),
},
};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would do

return {
  message: testEvaluatorResults.description,
  debuggingInfo: {
				 challengeLabel,
          metaTestId,
          internalTestId,
          internalTestLabel,
          userPassed: testEvaluatorResults.userPassed,
          evalResultType: testEvaluatorResults.evalResultType,
					evalError: testEvaluatorResults.evalError,
  }
};

I don't understand why you're double defining things with null the first time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the tip.
Is null better than undefined? I was under the impression it was better to have values be null vs undefined.

let evalResult: unknown;
let evalError: unknown;
const formattedCode = getCode(metaCaseCode, removeComments);
const logs = [] as Array<unknown>;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was to lazy to fix this. Can you make it not unknown?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I changed it to string.

}

restoreConsoleLog();
return assignReturnObject();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when a function gets something, aka returns something, then you prefce it with get. Here you'er implying that you're reassinging within the function instead of getting something

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok thanks. I changed the name to getMetaTestResultObject()

@@ -0,0 +1,156 @@
// Import filepaths changed from original
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't understand

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't either. I'm not sure I wrote this.

I'll remove all the commented / unused functions

}: {
condition: string;
}) => {
debugger;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't, it is currenty used in test-evaluators.ts

import {
  evaluateWithContext,
  getCode,
  getEvaluationContext,
  overrideConsoleLog,
  restoreConsoleLog,
} from "../code-challenge/utils";

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

debugger is?

const context = getEvaluationContext(code);
const logs = [] as Array<string>;
// Disabled for debugging test-evaluator

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit remove extra newline

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they are being added via auto-format, should that be disabled?

############################

src/api/code-challenge/content-types/code-challenge/*.js
!src/api/code-challenge/content-types/code-challenge/lifecycles.js
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say a clone of the src folder but let me know if you have issues

Comment on lines 2 to 4
if (error instanceof Error) return error.message;
return String(error);
};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In terms of formatting I would add a newline before the return statement

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it.

Comment on lines 8 to 9
const runMetaTests = async (eventParams) => {
const { eventTests, eventMetaTests, challengeLabel } = eventParams;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just do

const runMetaTests = async ({ eventTests, eventMetaTests, challengeLabel }) => {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed it.

const [internalTests, metaTests] = await Promise.all([
getInternalTests(eventTests),
getMetaTests(eventMetaTests),
]);
for (let i = 0; i < metaTests.length; i++) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Always have a newline before a for loop and a return statement, unless they are the first line in a function

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I've added newlines in other PR files for this suggestion too.

Comment on lines 15 to 22
let metaTestResult = await executeTests(
metaTests[i].caseCode,
internalTests,
metaTests[i].label,
challengeLabel,
metaTests[i].id,
metaTests[i].passes
);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you get 3 or more parameters, especially this amount, it's a good idea to have just one parameter, an object with properties. The benefit is that the order isn't important and the parameters have labels. With this many parameters it's hard to keep track of things

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a repeat of the comment below

Comment on lines 63 to 65
message: {
description: testEvaluatorResults.description,
},
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't need to be an object, it can just be

message : testEvaluatorResults.description

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the catch.

Comment on lines 89 to 91
/* Originally, the content of internalTest === internalTestCode in strapi.
I renamed internalTest to internalTestCode for clarity in lifecycles.js & test-evaluator.ts
This renamed variable makes: (internalTestCode vs internalTestLabel vs internalTestId) the children of internalTestPackage */
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you do multi line comments you line it up like:

/**
 * wefweewfe
 * wfwefw af awe
 */

Notice the * at the beginning of each line

Copy link
Contributor Author

@andypants888 andypants888 Jul 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the tip. I changed the *'s

Should I do this for 2+ lines or 3+ lines long multi-line comments?

Comment on lines 93 to 97
.map((internalTestPackage) => {
internalTestPackage.internalTestCode = internalTestPackage.internalTest;
delete internalTestPackage.internalTest;
return internalTestPackage;
})
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't delete and add properties, return a new object:

=> ({
  internalTestCode: internalTestPackage.internalTest,
  ...omit(internalTestPackage, 'internalTest')
})

but also, why are you calling it internalTestPackage? Just call it internalTest. It is confusing to have all these arbitrary renamings. The less renaming, the easier it is for someone to keep track of things

This renamed variable makes: (internalTestCode vs internalTestLabel vs internalTestId) the children of internalTestPackage */
return internalTests
.map((internalTestPackage) => {
internalTestPackage.internalTestCode = internalTestPackage.internalTest;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of the big comment there I would just add a comment above this line saying:

renaming the property from `internalTest` to `internalTestCode` for clarity

That's all you need, the rest just adds more to read. Less is more

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I added in your abridged comment

Comment on lines 45 to 61
label: internalTestLabel,
internalTestCode,
id: internalTestId,
} = internalTest;

// console.log("<internalTest pMap> ", internalTest);
// console.log("newTest name: ", newTest);

const testEvaluatorResults = await runTestEvaluator({
metaCaseCode,
internalTestCode,
metaLabel,
challengeLabel,
metaTestId,
internalTest,
evalResultShouldBe,
});
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So thinking about internalTest vs internalTestCode more I have two thoughts:

  1. The most clear thing to do would be to probably rename the property internalTest to just, code, in the strapi backend. However that's a bit time consuming so it's not worth doing.
  2. It seems like the only time where internalTest isn't the best property name is here, because we already have a variable called internalTest. I would say that if you don't destructure here, then you don't have that issue. That's not the most ideal, but it's the lesser of two evils and it's pretty common to avoid awarkdness like this. I mean, renaming a property is more awkward and confusing than that. So yeah just to reiterate, it's ok to avoid desctructuring to avoid awkwardness.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And yeah, just get rid of the renaming and don't desctructure here

Copy link
Contributor Author

@andypants888 andypants888 Jul 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I got rid of the renaming structure, but struggling to remove the variable that stores internalTest.internalTest, since I can't pass in

{
  internalTest.internalTest: "string here" // error!
}

or rename internalTest = internalTest.internalTest without wiping out the object.

Let me know what you think.

I left the declarations regular for clarity.

Copy link
Owner

@oriooctopus oriooctopus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approved with a couple comments

}: {
condition: string;
}) => {
debugger;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

debugger is?

Comment on lines 60 to 65
const eventParams = {
eventTests: event.params.data.tests,
eventMetaTests: event.params.data.MetaTest,
challengeLabel: event.params.data.internalLabel,
};
runMetaTests(eventParams);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you don't need to have a separate line here, just do

 runMetaTests({
    eventTests: event.params.data.tests,
    eventMetaTests: event.params.data.MetaTest,
    challengeLabel: event.params.data.internalLabel,
  });

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the catch.

Comment on lines 34 to 36
const getNoError = () => {
return "no eval() error detected";
};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't need to be a function it can just be a string

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks again.

Comment on lines 56 to 63
case typeof evalResult !== "boolean" && typeof evalResult !== "string":
return {
evalError: evalError ? getShortError() : getNoError(),
userPassed: false,
description: `FAIL: EvalResultType should always be 'boolean' but is currently '${typeof evalResult}'.`,
evalResultType: typeof evalResult,
evalResult: evalResult,
};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would honestly just put this as the last 'default' case but that might just be a matter of opinion

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that makes sense. I changed it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants