Fork me on GitHub
#nbb
<
2022-08-19
>
genRaiy10:08:12

I am having some bizarre behaviour when trying to order test fixtures

genRaiy10:08:28

(defn -main
  [_]
  (p/let [db-ok? (db-fixture/create-test-db)]
    (when db-ok?
      (-> (p/let [;; Run DB required tests. In order for simpler reporting
                  _ (upload-pre-selected-customer-test/run-tests)])
          (p/finally
            (fn [_ _]
              (when db-ok?
                (db-fixture/drop-test-db))))))))

genRaiy10:08:07

if I have one deftest in the upload-pre-selected-customer-test ns, it all works fine

genRaiy10:08:19

if I have two ... all hell breaks loose

genRaiy10:08:58

any ideas would be welcome cos I'm having a real head scratcher here

borkdude11:08:11

More details or repro? Afk now but can check later when you have one

genRaiy11:08:12

will try to make a repro

genRaiy16:08:53

(defn -main
  [_]
  (p/let [;; Run non-DB tests. In order for simpler reporting
          p1 (p/promise 1)]
    (when p1
      (-> (p/let [;; Run DB required tests. In order for simpler reporting
                  tests1 (postgres-test/run-tests)]
            (js/console.log "TESTS conclude" tests1))
          (p/finally (fn [_ _]
                       (js/console.log "FINALLY Called Now")
                       (p/promise 1)))))))

genRaiy16:08:08

replaced postgres tests with this

genRaiy16:08:23

(ns postgres-test
  (:require
    [cljs-bean.core :as cb]
    [cljs.test :as t :refer [async deftest is testing]]
    [postgres]
    [promesa.core :as p]))


(deftest p1
  (testing "p1"
    (async done
      (-> (p/let [p1 (p/promise 1)]
            (js/console.log "p1")
            (is (= 1 p1)))
          (p/finally done)))))


(deftest p2
  (testing "p2"
    (async done
      (-> (p/let [p1 (p/promise 1)]
            (js/console.log "p2")
            (is (= 1 p1)))
          (p/finally done)))))


(defn run-tests
  []
  (t/run-tests 'postgres-test))

genRaiy16:08:41

I checked this with other tests and any time you have more than one deftest using a promise the finally executes after the first promise is resolved

genRaiy16:08:02

npx nbb --classpath src:test -m runner Testing postgres-test p1 TESTS conclude true p2 FINALLY Called Now Ran 2 tests containing 2 assertions. 0 failures, 0 errors.

genRaiy16:08:33

p2 is called after tests conclude and then the finally and the p2 are in a race

genRaiy16:08:00

so big question for me ... am I holding it wrong?

borkdude16:08:14

I'll try locally

borkdude16:08:06

When I run this:

(ns postgres
  (:require
   [cljs.test :as t :refer [async deftest is testing]]
   [promesa.core :as p]))


(deftest p1
  (testing "p1"
    (async done
           (-> (p/let [p1 (p/promise 1)]
                 (js/console.log "p1")
                 (is (= 1 p1)))
               (p/finally done)))))


(deftest p2
  (testing "p2"
    (async done
           (-> (p/let [p1 (p/promise 1)]
                 (js/console.log "p2")
                 (is (= 1 p1)))
               (p/finally done)))))


(t/run-tests 'postgres)
I get:
$ nbb /tmp/postgres.cljs

Testing postgres
p1
p2

Ran 2 tests containing 2 assertions.
0 failures, 0 errors.
which looks good?

borkdude16:08:42

If you want to provide a more complete repro, please make a github repo which I can clone locally so we're speaking about exactly the same thing

borkdude16:08:35

sorry, I didn't notice you posted multiple files

borkdude16:08:52

it would be easier for me to be able to clone it though, than to replicate the same setup by copy/pasting things from slack

1
genRaiy17:08:34

I'll make a small repo

genRaiy18:08:46

npx nbb -m runner

Testing promise-test
p1
TESTS conclude true
p2
FINALLY Called Now

Ran 2 tests containing 2 assertions.
0 failures, 0 errors.

borkdude18:08:48

That's what I'm seeing locally. What is unexpected?

genRaiy18:08:56

no harm here but bad things happen when p2 is not resolved reliably before the let bindings are completed

genRaiy18:08:26

should see p1, p2 ... then TESTS concude

genRaiy18:08:08

unless I am missing something about the p/let contract

borkdude18:08:15

ah ok, now looking into it then

genRaiy18:08:53

thanks... I would love for it to be one of these

borkdude18:08:01

run-tests does not return a promise, so awaiting that doesn't really wait on anything

genRaiy18:08:28

(defn run-tests
  []
  (p/let [_ (t/run-tests 'promise-test)]
    true))

borkdude18:08:33

> >

To detect test
> completion add a :end-run-tests method case to the cljs.test/report
> multimethod.
>

genRaiy18:08:34

does the job

borkdude18:08:12

This returns true immediately, before the tests run (or somewhere in the middle)

genRaiy18:08:19

ha! ok, thanks ... I'll do that too

borkdude18:08:40

It's indeed a facepalm but mostly because CLJS doesn't really embrace promises here

borkdude18:08:12

You could solve this by resolving a promise inside that multimethod though

borkdude18:08:26

It would be pretty nice to have a promise-based test runner instead of the cljs.test one

💯 1
genRaiy18:08:32

yes, that's what I'll do

genRaiy19:08:13

thanks for your help - totally excellent

❤️ 1
genRaiy19:08:13

(defn run-tests
  []
  (p/let [_ (t/run-tests 'promise-test)]
    true))

genRaiy19:08:22

reliably produces the correct results

genRaiy19:08:40

adding a promise to that multimethod has no effect

genRaiy19:08:01

(defmethod t/report [::t/default :end-run-tests]
  [m]
  (p/let [ok? (t/successful? m)]
    (if ok?
      (println "Tests passed!")
      (println "FAIL"))))

genRaiy19:08:16

no change in the chaos

borkdude19:08:22

Do you see the println?

genRaiy19:08:18

Does it work for you?

borkdude19:08:10

What you're seeing is purely co-incidental, just add a _ (p/delay 1000) in one of those tests and you'll see that it doesn't work with p/let + true

1
borkdude19:08:45

This works:

(def end-resolve (atom nil))
(def end-promise (js/Promise. (fn [resolve _reject]
                                (reset! end-resolve resolve))))

(defmethod t/report [:cljs.test/default :end-run-tests]
  [m]
  (let [ok? (t/successful? m)]
    (if ok?
      (println "Tests passed!")
      (println "FAIL"))
    (@end-resolve)))

(defn run-tests
  []
  (t/run-tests 'promise-test)
  end-promise)

borkdude19:08:57

So, in the multimethod you resolve the promise and in run-tests you return the promise that will be fulfilled

1
genRaiy21:08:04

Blimey, I'm gonna need a minute 🤯 Thanks for digging into it. Like you say, dealing with this stuff is annoying compared to what a promise based library could offer.

genRaiy11:08:51

It is a mess when there are two test nses with N deftests

genRaiy11:08:37

both the clause after the let bindings and the finally on the runner never executes

borkdude11:08:23

Repro or it didn't happen. :)

genRaiy11:08:04

actually I think the finally shit is me holding it wrong - I should use per ns fixtures instead

genRaiy11:08:13

In this case we should be seeing some console output

genRaiy11:08:19

(ns runner
  (:require
    [promesa.core :as p]
    [promise-second-test]
    [promise-test]))

(defn -main
  [_]
  (-> (p/let [_ (promise-test/run-tests)
              _ (promise-second-test/run-tests)]
        (js/console.log "TESTS conclude"))
      (p/finally (fn [_ _]
                 (js/console.log "Finally TESTS conclude")))))

genRaiy11:08:25

but there is none

genRaiy11:08:49

it's more of a curiosity at this point cos the tests and fixtures all run and behave how I want

genRaiy11:08:59

I have pushed the updates

borkdude16:08:05

I'm seeing:

$ nbb -m runner

Testing promise-test
stand up promise-test
tear down promise-test
Tests passed!

Ran 3 tests containing 3 assertions.
0 failures, 0 errors.

Testing promise-second-test
stand up promise-second-test
tear down promise-second-test
Tests passed!

Ran 2 tests containing 2 assertions.
0 failures, 0 errors.
Can you explain what is unexpected?

borkdude16:08:21

oh yes, I see, the console logs from the runner

1
borkdude16:08:23

when I remove #_#__ (promise-second-test/run-tests) I do see the output... hmm

borkdude16:08:31

so what you're doing here is that a defmethod in the first ns overrides the defined method of the other ns

genRaiy17:08:39

Yes, like I say it gets messy when you do more things. Usually you should be able to scope these things to the specific ns

genRaiy17:08:11

or maybe more to the point that's what I would like to be able to do

genRaiy17:08:04

the whole point of this is to run two test suites with promises and for them to conclude in an orderly manner

genRaiy17:08:37

otherwise the async is leaking

genRaiy17:08:54

it's not so urgent in the sense that we have a workable test suite now but I feel like the silent console is trying to tell us something

borkdude18:08:53

This is probably because you're overriding one defmethod + dispatch with another one so one of the promises never gets fulfilled

borkdude18:08:35

If you want to have one promise per ns, you should resolve a namespace specific promise in the defmethod

1
borkdude18:08:43

If you like to run tests from different namespaces in order, you can just do:

(run-tests 'namespace-one 'namespace-two)

genRaiy06:08:13

I should just run all tests. I think this runner is overly complex

genRaiy10:08:29

that test runner plus the fixtures .... everything just seems to work without the need for the promise resolving hack

👍 1
genRaiy16:08:02

I know I could do it but if you have a version that runs all the test nses .... would be nice

genRaiy16:08:41

not to worry, I've adjusted it

genRaiy13:08:45

ha, the adjustments are falling over after I have three. 😿

genRaiy13:08:14

forget that ... just using run-tests after the various adjustments is working

genRaiy15:08:53

You probably know already but all of the promise hacks are not needed in that case

👍 1
sirwobin12:08:44

Is there a simple way to include core.async in nbb scripts?

borkdude12:08:14

core.async isn't supported in nbb. the recommended approach is to use promises directly in combination with promesa.core

sirwobin12:08:33

Righto, thank you.

jaide21:08:44

I recall there was an example out there of building a binary from a nbb script. If I remember right, the library for that started with a c and is an alternative to something like nexe. Anyone remember what it was?

jaide21:08:57

Thanks! Exactly what I was trying to remember. Was looking in the examples folder, should have also checked docs.

borkdude21:08:09

no problem :)