firebaseをどうテストするか
firebaseをいじってて少しだけ知見がたまってきたので共有します。
前提
ローカルでテストしたいなら、Firebase Local Emulator Suite を使いましょう。
yarn run firebase emulators:start
で起動できます。
yarn firebase emulators:exec 'yarn test'
ってやると、emulator起動してからテストしてくれます。
設定
これを書いておきましょう。ローカルでの起動でemulatorを使うようになります。create-react-appを使ってるのでNODE_ENV
で分岐してますが、別に他のでもいいです。あとポートは自分のに合わせましょう。
const app = firebase.initializeApp(firebaseConfig); export const db = app.firestore(); if (process.env.NODE_ENV !== "production") { db.settings({ host: "localhost:8080", ssl: false, }); app.functions().useFunctionsEmulator("http://localhost:5001"); }
firestore
rulesのテストは簡単です。@firebase/rules-unit-testing
を使えば下のようなコードですぐテストできます。(@firebase/testingから改名しました)
import { assertFails, initializeTestApp, } from "@firebase/rules-unit-testing"; test("denied if not authed", async () => { const notAuthedDB = initializeTestApp({ auth: { uid: undefined } }).firestore(); const userRef = notAuthedDB.doc("/users/uid"); await assertFails(userRef.set({ hoge: "hoge" })); });
もしrulesを回避して先にテスト用データを用意しておきたいなら、initializeAdminApp
を使いましょう*1。
firebase.rules
はちゃんと設定している限り自動で読み込まれるので、loadFirestoreRules
をbeforeEach
で読み込んだりする必要はないはずです。
問題なのが、以下のような関数をテストする場合です。
export const complexTransaction = async () => { // 複雑なDB操作... }
これに関してはスマートな解決策を見つけられていません。一応jestの場合は、
jest.mock("firebase/app", () => { // eslint-disable-next-line @typescript-eslint/no-var-requires const { initializeTestApp, firestore } = require("@firebase/testing"); const mockedApp = initializeTestApp({ projectId: "pid", auth: { uid: "uid" }, }); return { initializeApp: () => mockedApp, firestore: firestore, }; });
というようにmockすればなんとかテストできますが、あまり綺麗ではないですね。最悪adminでfirebase.firestore
をemulatorにつなげてくれればいいんですが……。
functions
import * as admin from "firebase-admin"; import * as funcTest from "firebase-functions-test"; const db = admin.firestore(); const tester = funcTest({ projectId: process.env.GCLOUD_PROJECT }); const makeDocumentSnapshot = tester.firestore.makeDocumentSnapshot; const makeChange = tester.makeChange; const wrapped = tester.wrap(yourAwesomeFunction); test("assert Firestore", async () => { // arrange const roomId = "sefasef"; const roomPath = `rooms/${roomId}`; const beforeData = { hoge: "hoge", }; const before = makeDocumentSnapshot(beforeData, roomPath); const afterData = { hoge: "fuga", }; const after = makeDocumentSnapshot(afterData, roomPath); const change = makeChange(before, after); // act await wrapped(change, { params: { roomId } }); // assert const roomRef = db.doc(roomPath); expect((await roomRef.get()).data()).toEqual(afterData); });
こんな感じでいけます。functionsのテストは比較的簡単です。あと、Warning, estimating Firebase Config based on GCLOUD_PROJECT. Initializing firebase-admin may fail
と警告がでます。これは正直わからん。。。
*1:initializeTestAppのprojectIdと同じもので初期化する必要があります
firebase emulatorにauthenticationはまだ無いよ
https://github.com/firebase/firebase-tools/issues/1677
しばらくfirebase authをエミュレートする方法探してたけど、上のissueにあるように、まだ実装されてない。
やる気は有るようなので待つしかないなーというかんじ
でもログイン回りのテストどうしようか・・・
AVIOT TE-D01g のペアリング方法
左右分離式なので、結構ペアリングが上手くいかなかったりしてめんどいので、うまくいく経験的なやり方を書きます。
- 親機からペアリング解除し、イヤホンと親機の電源を切る(二回効果音が鳴るまでボタン長押しで電源off)
- イヤホンを左右ともペアリング状態にする(3回なるまで長押しでペアリング状態)
- 両方ともペアリングする。(説明書には片方でいいと書いてあったはず)
これが一番安定する気がします。
ちなみに再ペアリングすると、同じデバイスでも2デバイス扱いになるので、最後から3回目につなげたデバイスがイヤホンのメモリから削除されます。
片方だけペアリングしてもうまくいかないのはなぜ~~~~。
追記
両方電源ON→片方だけペアリング でも上手くいくことが最近判明しました。よかったですね。
Windows 10 EducationでMicrosoft Storeを利用する
大学配布のwinライセンスを使っているのでEducationになっているのですが、大学側で一部の利用が制限されている(?)ようで、レポート中でもwindows updateで強制再起動させられたりしています。
中でも一番困ったのが、Microsoft Storeが使えないことで、待ちに待ったWSL2とwindows terminalを入れられないというもどかしい思いをしていました。調べてみると一応解決したのでのせておきます。これがほかの人の環境でも有効なのかはわかんないです。
ここを見ましょう。 簡単に言うと、
- https://educationstore.microsoft.com/ja-jp/storeに行く。
- 学校のために購入タブからアプリを検索。(Ubuntuなど)
- アプリの入手をクリック
- 「インベントリに追加されました」と出るので、インストールをクリック
- 「Microsoft Storeを開く」をクリック
でできると思います。
注意として、microsoft storeのアカウントなのですが、これは自分のアカウントではなく、大学や会社など、組織のアカウントじゃないとだめです。大学の場合はwinのライセンスもらうときとかに配布されるんじゃないかなと思います。忘れたら・・・うん。
Djangoの設定のベタープラクティス
Djangoをデプロイする時の設定です。ベストプラクティスじゃなくて(そのままよりは)ベタープラクティスです
絶対に漏れ、抜けがあるので参考程度に見てください。公式を見るのが最も安全です。
settings.pyの分割
Djangoの本番/開発設定を切り分けるにはsettings.pyを開発用、本番用に分割するのが良いようです。実際DjangoのCMSであるWagtailもそのような構成になっています。
普通にDjangoでプロジェクトを作ると、
$ django-admin startproject myprj $ tree myprj myprj/ ├── manage.py └── myprj ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py
となりますが、Wagtailでは
$ wagtail start myprj
$ tree myprj
myprj/
├── manage.py
├── myprj/
│ ├── __init__.py
│ ├── settings/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── dev.py
│ │ └── production.py
│ ├── urls.py
│ └── wsgi.py
というように、共通の設定(base.py)を用意して、開発用(dev.py)と本番用(production.py)で設定を切り替えています。
そもそもなんで開発/本番で設定を切り替えたいかというと、
- 本番ではDEBUG=Falseにする
- SECRET_KEYを隠したい
- ALLOWED_HOSTSを設定する
など、開発/本番で違う点があるためです。
settingsの中身
具体的に、dev.pyは以下のようになっています。
from .base import * # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'o4ei=g0^4cu88sw$@rwuy*gh!4lu7ex74#0t!h^0=fy2k$4-(2' # SECURITY WARNING: define the correct hosts in production! ALLOWED_HOSTS = ['*'] EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' # ここはよくわかんない。ローカル専用の設定かな? try: from .local import * except ImportError: pass
前述したとおり DEBUG=Trueなのと、ALLOWED_HOSTS=['*']です。 それにSECRET_KEYが直書きですね。
コメントにSECURITY_WARNINGとあるように、この設定は本番で使ってはいけません。また、SECRET_KEYを公にしてもいけません。もしGithubなどに公開してしまったら、別の値に変えた上で、dev.pyをリポジトリから消しましょう*1。
ちなみにdev.pyのファイル名についてですが、ここにあるように、local_settings.pyとするのが一般的なようです。
つぎにproduction.pyです
from .base import * DEBUG = False try: from .local import * except ImportError: pass
これだけ。もはや何もかいてないです。ちゃんと自分で設定しろということでしょう。
- DEBUG=False
- SECRET_KEY
- DATABASE
- ALLOWED_HOSTS
- STATIC_ROOT
- STATIC_URL
とかを設定すれば動くはずです。多分。
なお、Herokuにデプロイする時はdjango-herokuがよしなにやってくれますが、動作を知りたい人は以前の記事で書いたので読んでみてください。
settingsを使うには
多分分割しただけだとpython manage.py <command>
したときにファイルが見つかんないよ的なエラーが出るとおもいます。
manage.pyを以下のように変えてください。
# もともとこうなっているはずなので os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myprj.settings') # このように設定ファイルの相対パスを指定する os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myprj.settings.dev")
dev/productionはやはり開発/本番で変えるはずなので、try-catchで、dev.pyがあればそっちを使うなどのように条件分岐させてみてください。
ちなみにwsgi.pyやasgi.pyなども同じように設定する必要があります。
*1:それがセキュリティ的に正しいのかはわかりませんが...
Folding@homeに参加しました。手順とトラブルシューティング。
Folding@home(fah)に参加しました。化石と化していた1050tiを使っています。
Folding@homeとは、タンパク質構造予測に必要な計算処理を、世界中の有志による分散コンピューティングで推進するプロジェクトです。 10年以上昔の話になりますが、PlayStation 3を活用したプロジェクトを覚えている方も多いかと思います。
Folding@homeを通じてCOVID-19治療薬の発見に貢献します - Cybozu Inside Out | サイボウズエンジニアのブログ
参加方法
僕はubuntuを使っているので、MacやWindowsはあんまりわかんないです。多分インストールして、Foldで出来ると思います。
- ここから自分にあったインストーラーをDLする。
- Linux系は最低限、fahclientとfahcontrolをDLする。fahviewerはなくても良い。(多分)
- statusタブを開いて、Foldをクリックする。
FAQ
client, control, viewerの違いはなんですか?
それぞれが左のソフトウェアに依存しています(多分)。clientがコアソフトウェアで、controlが制御用、viewerでこんなのが見れます。
GPUが使われないのですが?
まずドライバをインストールしましょう。
次に
sudo apt install ocl-icd-opencl-dev
してください。
そして、
clientを起動->configure->Expert->Extra Client Optionsの、gpuがfalseになっているならtrueにする->再起動
をやってください。
これでGPUを使ってFoldできるならOK。出来ないならconfigure -> Slots -> Folding Slots-> 追加 ->gpu
を試してみてください。
これでも出来ないならわからないです...
GPUのステータスを見たい
nvidia-smi -l 3600
で1時間ごとに使用率とか、温度とかが見れます。
Foldが始まりません
Notice your @foldingathome installation is idle? So many people have joined in to support our work on #COVID19 that our servers are struggling to keep up. We're continuing to get more online as quickly as we can. We still need your help, so please be patient with us. Please RT.
— Greg Bowman (@drGregBowman) 2020年3月26日
結構中央サーバーにアクセスが集中しているようで、必要なデータをDLするまで時間がかかります。放置してると始まるかも。
ERROR:WU01:FS01:Exception: Could not get an assignment
ってエラーがでたりします。
何を計算しているんですか?
Let’s talk about how simulating proteins with Folding@home fits into drug discovery and development.
— Folding@home (@foldingathome) 2020年3月27日
[Thread] 1/11
解決できません!
公式フォーラムに行きましょう。
ちなみに、
EFLOPSレベルまでいったそうです。
Thanks to our AMAZING community, we’ve crossed the exaFLOP barrier! That’s over a 1,000,000,000,000,000,000 operations per second, making us ~10x faster than the IBM Summit! pic.twitter.com/mPMnb4xdH3
— Folding@home (@foldingathome) 2020年3月25日
ReactをHerokuに秒速でデプロイしたい
create-react-appで作ったプロジェクトに限った話です!!!!!!!
ここに書いてあるまんまですが、
APP_NAME=すごいアプリの名前 npx create-react-app $APP_NAME cd $APP_NAME heroku create $APP_NAME --buildpack mars/create-react-app git push heroku master heroku open
--buildpack mars/create-react-appが一番大事なところなので忘れないように。ちなみに既にあるHerokuアプリでcreate-react-appを使いたい時は、
heroku buildpacks:set mars/create-react-app
です*1。
django-herokuは何をやっているのか
django-herokuに頼らず自分で設定していきたいので、まずはdjango-herokuが何をやっているか確認していきます。
環境変数という語がでてきますが、これはherokuのダッシュボードもしくはCLIから割り当てられる変数で、(おそらく)セキュアです。os.environ[<key>]
で取得できます。
49行目 にsettings関数が有りますね。この中を読んでいけばよさそうです。
まず引数ですが、configにはlocals()が渡されるので、つまりはsettings.pyの名前空間です。
53行目からはデータベースの設定のようです。
loggerを飛ばして見ていくと、75行目で大事な設定をしているのがわかります。
config['DATABASES']['default'] = dj_database_url.config(conn_max_age=conn_max_age, ssl_require=True)
ここでdj_database_url.configという関数がでてきますが、これは環境変数の$DATABASE_URLをURLとか、パスワードとかの部分に分解して、djangoの設定に合うようにパースしてくれています。
これでデータベースは終わりです。
次に92行目からstaticfileの設定をしているようです。
95行目からは以下のようなコードです。
config['STATIC_ROOT'] = os.path.join(config['BASE_DIR'], 'staticfiles') config['STATIC_URL'] = '/static/' # Ensure STATIC_ROOT exists. os.makedirs(config['STATIC_ROOT'], exist_ok=True) # Insert Whitenoise Middleware. try: config['MIDDLEWARE_CLASSES'] = tuple(['whitenoise.middleware.WhiteNoiseMiddleware'] + list(config['MIDDLEWARE_CLASSES'])) except KeyError: config['MIDDLEWARE'] = tuple(['whitenoise.middleware.WhiteNoiseMiddleware'] + list(config['MIDDLEWARE'])) # Enable GZip. config['STATICFILES_STORAGE'] = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
STATIC_ROOTとSTATIC_URLの設定をして、STATIC_ROOTのフォルダを作っていますね。
そのあとwhitenoise*1をmiddlewareに追加して、STATICFILES_STORAGEもwhitenoiseのものに差し替えています。
110行目からはALLOWED_HOSTSです。
if allowed_hosts: logger.info('Applying Heroku ALLOWED_HOSTS configuration to Django settings.') config['ALLOWED_HOSTS'] = ['*']
あーよくない。ALLOWED_HOSTS=['*']にしているのでよくないです。なにが良くないのかはわかんないです。
テスト環境ならこれでも良いでしょうが、本番の場合はちゃんとホストするアドレスを指定しておきたいですね。
114行目からはロガーの設定なので割愛。
151行目以下でSECRET_KEYです。
if secret_key: if 'SECRET_KEY' in os.environ: logger.info('Adding $SECRET_KEY to SECRET_KEY Django setting.') # Set the Django setting from the environment variable. config['SECRET_KEY'] = os.environ['SECRET_KEY']
環境変数から取ってきたSECRET_KEYをconfigに割り当てています。間違ってもSECRET_KEYを公開しないようにしましょう。
これをsettings.pyに持ってくればherokuにデプロイできます。
まとめると
STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, 'static') STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' MIDDLEWARE = [ # ... 'whitenoise.middleware.WhiteNoiseMiddleware', # ... ] DATABASES = { 'default': dj_database_url.config(conn_max_age=CONN_MAX_AGE, ssl_require=True) } SECRET_KEY = os.environ.get("SECRET_KEY")
のようになります。whitenoiseのインストールや、環境変数の割当を忘れないでください。
最近やったこと
結構ネタバレがある
読んだ本と見た映画
kindleでハヤカワセールやってるのでSFを読んでる。kindleやばい。いつでも読めるのでいつでも読むことが出来る。
- 作者:ロバート A ハインライン,福島 正実
- 発売日: 2013/05/24
- メディア: Kindle版
猫の描写が良かった。飼ったこと無いけどあるある〜〜〜〜。てなった。
典型的なタイムトラベル物で、最後に伏線を全部回収していくのはやはり爽快。最後がハッピーエンドなので良い。頑張った後に金と女に恵まれる話っていいよね。
序盤はコールドスリープしか出てこなかったのでどうなるんだろうな〜と思ってたら途中からタイムマシンが出てきた。いやそれ最初からコールドスリープいらなかったのでは。
古典SFあるあるだけど、記述が冗長。刊行当初なら新鮮味があるだろう描写も、もはや現実の方が追い越しているので目新しさはない。
思弁系か娯楽系かでいうと圧倒的に娯楽系だったのでよみやすかった。
アメリカにアンクル・サムってふりがな振ってあるの格好良かったのでどこかで使っていきたい。
- 作者:スタニスワフ・レム
- 発売日: 2015/04/08
- メディア: 文庫
夏への扉と真反対の思弁系。最初の、お客さんの正体がわかるまではホラーとしてめちゃめちゃ楽しかった。わかってからは一気にSpeculative Fictionと化した。
ソラリス学はまぁ冗長だけど必要ではあると思う。あれのおかげでソラリスの特異性が強調される。
この本は様々な読み方をすることが出来る傑作SFというのが一般的な評価らしい。円城塔のいうところの、全てを説明しうるがゆえに何も説明できない概念というのを絡めてなにかもっともらしく論評しようとしたけどどうにもならなかったので辞めた。
スナウトとサルトリウスのお客さんは誰だったのかな。スナウトは動物っぽいと思う。
解説がちゃんと解説してて本当に良かった。解説とタイトルをつけてポエム書いてる著者まじで辞めてくれ。
衝動的に見てみた。勿論原典ではないけど、再現されたヒトラーを見ていて、やっぱ演説が上手いんだなーとか思った。
序盤はタダのコメディで、後半にかけて雲行きが怪しくなっていくという展開も好き。
作中のヒトラー自身は何ら具体的な計画を語ってはいないけど、あまりに演説が上手いので自分の意見を代弁してくれている気がしてきた。
民主主義は国民の不断の努力によって維持されていかなければ行けないんだなぁというお気持ち。
本を読むのは良いけど、構造的に展開を把握できてないよなぁと思ったので読んだ。面白さを作るにはどんでん返しが必須というのは納得。あとは主人公の設定の仕方とか、敵の作り方とか。
純文学とか思弁SFとか、この本の解説から逸脱してるようなのもよくあるが、そういう小説を書くのはめちゃめちゃ難しいだろうな。
- 発売日: 2015/09/01
- メディア: Kindle版
確定申告で悩んだので。街には沢山の店があるから開業は簡単そうに思ってたけど、実際は複雑だとわかった。あんなに店があることが信じられない。あと渋谷でどうやってテナント料払ってるのかわからない店だらけなの謎すぎる。
- 発売日: 2014/02/07
- メディア: Kindle版
FGOでよく出てくるから読んだらいきなりアイアイエーイベ始まってびっくりした。読めば読むほど神が人間の味方だったことなんて一度もありませんよという気持ちになる。狂ってる?それ誉め言葉ね。
イアソンってすごい人なんだなとか、ヘラクレスは世界一強いんだから!とか、ゼウスカスすぎやろとか思うところが多い。あとはケルト神話と北欧神話と日本の偉人とアーサー王伝説とインドと中国とその他諸々の物語を読まなきゃいけない......
ちなみにkindleで読んだけど固定レイアウトだったのが辛かった。
やってること
- バイトを始めて病んでる
- django+react+graphQLでSPA作ろうと思ってるけど、ルーティングとかデプロイとかセキュリティで詰まってる。
- 運転の練習をしてる